1. 程式人生 > >spring security(八) session 併發,剔除前一個使用者

spring security(八) session 併發,剔除前一個使用者

解決 session 併發問題 ,同時只有一個使用者線上。 有一個使用者線上後其他的裝置登入此使用者將剔除前一個使用者。強制前一個使用者下線。

1.修改security配置

新增 SessionRegistry,自己管理SessionRegistry。

@Configuration
@EnableWebSecurity
@EnableGlobalMethodSecurity(securedEnabled = true)
public class WebSecurityConfig extends WebSecurityConfigurerAdapter {

    @Autowired
private CustomUserService customUserService; @Autowired SessionRegistry sessionRegistry; @Autowired protected void configure(AuthenticationManagerBuilder auth) throws Exception { auth.userDetailsService(customUserService).passwordEncoder(new BCryptPasswordEncoder()); } @Override
protected void configure(HttpSecurity http) throws Exception { http .csrf().disable() .authorizeRequests() .antMatchers("/users/**") .authenticated() .antMatchers(HttpMethod.POST) .authenticated() .antMatchers(HttpMethod.PUT) .authenticated() .antMatchers(HttpMethod.DELETE) .authenticated() .antMatchers("/**"
) .permitAll() .and() .sessionManagement().maximumSessions(1).sessionRegistry(sessionRegistry); http.httpBasic(); } @Bean public SessionRegistry getSessionRegistry(){ SessionRegistry sessionRegistry=new SessionRegistryImpl(); return sessionRegistry; }

2.LoginController

修改登入,登入成功後清除前一個線上使用者(dropPreviousUser 清除前一個使用者)

@RequestMapping(value = "/login")
    @ResponseBody
    //使用者名稱密碼是用base64 加密 原文為 admin:admin 即 使用者名稱:密碼  內容是放在request.getHeader 的 "authorization" 中
    public Object login(@AuthenticationPrincipal SysUser loginedUser, @RequestParam(name = "logout", required = false) String logout,HttpServletRequest request) {
        if (logout != null) {
            return "logout";
        }
        if (loginedUser != null) {
            SessionUtil.dropPreviousUser(request,sessionRegistry,loginedUser);
            return loginedUser;
        }
        return null;
    }

3.SessionUtil

session 管理工具類


/**
 * Created by yangyibo on 8/23/17.
 */
public class SessionUtil {

    /**
     * 辨別使用者是否已經登入
     *
     * @param request
     * @param sessionRegistry
     * @param loginedUser
     */
    public static void deleteSameUser(HttpServletRequest request, SessionRegistry sessionRegistry, User loginedUser) {
        SecurityContext sc = (SecurityContext) request.getSession().getAttribute("SPRING_SECURITY_CONTEXT");
        List<SessionInformation> sessionsInfo;
        sessionsInfo = sessionRegistry.getAllSessions(sc.getAuthentication().getPrincipal(), true);
        String currentSessionId;
        if (null != sessionsInfo && sessionsInfo.size() == 0) {
            sessionRegistry.registerNewSession(request.getSession().getId(), sc.getAuthentication().getPrincipal());
            sessionsInfo = sessionRegistry.getAllSessions(sc.getAuthentication().getPrincipal(), false);
        }
        currentSessionId = sessionsInfo.get(0).getSessionId();
        List<Object> o = sessionRegistry.getAllPrincipals();
        for (Object principal : o) {
            if (principal instanceof User && (loginedUser.getUsername().equals(((User) principal).getUsername()))) {
                List<SessionInformation> oldSessionsInfo = sessionRegistry.getAllSessions(principal, false);
                if (null != oldSessionsInfo && oldSessionsInfo.size() > 0 && !oldSessionsInfo.get(0).getSessionId().equals(currentSessionId)) {
                    for (SessionInformation sessionInformation : sessionsInfo) {
                        //當前session失效
                        sessionInformation.expireNow();
                        sc.setAuthentication(null);
                        sessionRegistry.removeSessionInformation(currentSessionId);
                        throw new GeneralServerExistException(ErrorMessage.ALONG_LOGIN_ERROTR.toString());
                    }
                }
            }
        }
    }

    /**
     * 剔除前一個使用者
     *
     * @param request
     * @param sessionRegistry
     * @param loginedUser
     */
    public static void dropPreviousUser(HttpServletRequest request, SessionRegistry sessionRegistry, User loginedUser, SysMessageService sysMessageService) {
        SecurityContext sc = (SecurityContext) request.getSession().getAttribute("SPRING_SECURITY_CONTEXT");
        List<SessionInformation> sessionsInfo;
        sessionsInfo = sessionRegistry.getAllSessions(sc.getAuthentication().getPrincipal(), false);
        if (sessionsInfo.size() > 0) {
            String  currentSessionId = sessionsInfo.get(0).getSessionId();
            List<Object> o = sessionRegistry.getAllPrincipals();
            for (Object principal : o) {
                if (principal instanceof User && (loginedUser.getUsername().equals(((User) principal).getUsername()))) {
                    List<SessionInformation> oldSessionsInfo = sessionRegistry.getAllSessions(principal, false);
                    if (null != oldSessionsInfo && oldSessionsInfo.size() > 0 && !oldSessionsInfo.get(0).getSessionId().equals(currentSessionId)) {
                        for (SessionInformation sessionInformation : oldSessionsInfo) {
                            //當前session失效
                            //send message
                            sysMessageService.sendMessage(((User) principal).getUsername(), new SysMessage(null, Consts.NOTIFICATION_TYPE_HADLOGIN_CONTENT, 5, Consts.NOTIFICATION_ACCEPT_TYPE_HADLOGIN));
                            sessionInformation.expireNow();
                        }
                    }
                }
            }
        }else {
            throw new  GeneralServerExistException(ErrorMessage.ALONG_LOGIN_ERROTR.toString());
        }
    }


    /**
     * session 失效
     *
     * @param request
     * @param sessionRegistry
     */
    public static void expireSession(HttpServletRequest request, User user, SessionRegistry sessionRegistry) {
        List<SessionInformation> sessionsInfo = null;
        if (null != user) {
            List<Object> o = sessionRegistry.getAllPrincipals();
            for (Object principal : o) {
                if (principal instanceof User && (user.getUsername().equals(((User) principal).getUsername()))) {
                    sessionsInfo = sessionRegistry.getAllSessions(principal, false);
                }
            }
        } else if (null != request) {
            SecurityContext sc = (SecurityContext) request.getSession().getAttribute("SPRING_SECURITY_CONTEXT");
            if (null != sc.getAuthentication().getPrincipal()) {
                sessionsInfo = sessionRegistry.getAllSessions(sc.getAuthentication().getPrincipal(), false);
                sc.setAuthentication(null);
            }
        }
        if (null != sessionsInfo && sessionsInfo.size() > 0) {
            for (SessionInformation sessionInformation : sessionsInfo) {
                //當前session失效
                sessionInformation.expireNow();
                sessionRegistry.removeSessionInformation(sessionInformation.getSessionId());
            }
        }
    }
}

4.session 失效

制定定時任務,定時使session 失效。失效機制,定時掃描線上使用者,判斷線上使用者的最後一次操作時間AccessLastTime 於當前的時間差是否超過 session失效時間 ,如果超過session 失效時間,將session 置為失效。

此處的session失效和 springboot 配置檔案裡配置的session 失效時間應當一致。(springboot 配置檔案裡配置的session 失效實際上並不能將我門自己管理的SessionRegistry 中的session失效。)

/**
     * 超時事件檢查
     */
    @Scheduled(cron = "0 0/1 * * * ?")
    public void ScanUserOnline() {
    //獲取所有線上使用者
        List<User> users = userDao.getUsersWithOnLine(4);
        users.stream().parallel().forEach(user -> {
        //通過時間判斷是否session過期
            if (CommonUtil.CalculationData(user.getAccessLastTime(),30)) {
                //如果過期則設定使用者為下下線狀態
                updateOnline(user.getId(),4,null);
            //session 置為失效  SessionUtil.expireSession(null,user,sessionRegistry);
            }
        });
    }