Springsecurity源码Filter之SessionManagementFilter(十六)

对Session控制和管理

配置

@Override
    protected void configure(HttpSecurity http) throws Exception {
        http.authorizeRequests()
                .anyRequest()
                .authenticated()
                .and().rememberMe()//记住登录
                .tokenRepository(new InMemoryTokenRepositoryImpl())
                .and()
                .formLogin()// rm表单的方式
                .loginPage("/login")//登录页面路径
                .loginProcessingUrl("/doLogin")
                //自定义登录请求地址
                .defaultSuccessUrl("/hello")
                .usernameParameter("loginName")
                .passwordParameter("loginPassword")
                .permitAll(true)//不拦截
                .and()
                .csrf()//记得关闭
                .disable()
                .sessionManagement().
                 maximumSessions(1) //需要这个字段设置为1
                .maxSessionsPreventsLogin(true);
    }

初始化处

org.springframework.security.config.annotation.web.configurers.SessionManagementConfigurer#configure

 public void configure(H http) {
        //SecurityContextRepository 可以参考默认是吸纳
        SecurityContextRepository securityContextRepository = http.getSharedObject(SecurityContextRepository.class);
        //getSessionAuthenticationStrategy 为Session的管理 可以参考实现 比如只允许一个用户在线的控制
        SessionManagementFilter sessionManagementFilter = new SessionManagementFilter(securityContextRepository,
                getSessionAuthenticationStrategy(http));
        //如果配置了出现异常 跳转的url则默认handle为SimpleUrlAuthenticationFailureHandler 底层控制异常跳转指定url
        if (this.sessionAuthenticationErrorUrl != null) {
            sessionManagementFilter.setAuthenticationFailureHandler(
                    new SimpleUrlAuthenticationFailureHandler(this.sessionAuthenticationErrorUrl));
        }
        
        InvalidSessionStrategy strategy = getInvalidSessionStrategy();
        if (strategy != null) {
            sessionManagementFilter.setInvalidSessionStrategy(strategy);
        }
        //异常的回调处理器
        AuthenticationFailureHandler failureHandler = getSessionAuthenticationFailureHandler();
        if (failureHandler != null) {
            sessionManagementFilter.setAuthenticationFailureHandler(failureHandler);
        }
        //校验当前会话是否可以处理 可以看一下默认实现
        AuthenticationTrustResolver trustResolver = http.getSharedObject(AuthenticationTrustResolver.class);
        if (trustResolver != null) {
            sessionManagementFilter.setTrustResolver(trustResolver);
        }
        //Autowried注入
        sessionManagementFilter = postProcess(sessionManagementFilter);
        http.addFilter(sessionManagementFilter);
        //如果return this.maximumSessions != null;
        if (isConcurrentSessionControlEnabled()) {
            ConcurrentSessionFilter concurrentSessionFilter = createConcurrencyFilter(http);

            concurrentSessionFilter = postProcess(concurrentSessionFilter);
            http.addFilter(concurrentSessionFilter);
        }
    }

org.springframework.security.web.session.SessionManagementFilter#doFilter(javax.servlet.http.HttpServletRequest, javax.servlet.http.HttpServletResponse, javax.servlet.FilterChain)

    private void doFilter(HttpServletRequest request, HttpServletResponse response, FilterChain chain)
            throws IOException, ServletException {
        //避免重复执行
        if (request.getAttribute(FILTER_APPLIED) != null) {
            chain.doFilter(request, response);
            return;
        }
        request.setAttribute(FILTER_APPLIED, Boolean.TRUE);
        //判断是否存在Context 多个实现 比如参考HttpSessionSecurityContextRepository<1>
        if (!this.securityContextRepository.containsContext(request)) {
            Authentication authentication = SecurityContextHolder.getContext().getAuthentication();
            //判断authentication 是否可以处理
            if (authentication != null && !this.trustResolver.isAnonymous(authentication)) {
                // The user has been authenticated during the current request, so call the
                // session strategy
                try {
                    //进行Session的校验 比如是否超过最大限制 多个实现 可以参考各个是实现<2>
                    this.sessionAuthenticationStrategy.onAuthentication(authentication, request, response);
                }
                catch (SessionAuthenticationException ex) {
                    // The session strategy can reject the authentication
                    this.logger.debug("SessionAuthenticationStrategy rejected the authentication object", ex);
                    //清空
                    SecurityContextHolder.clearContext();
                    //回调自定义error处理器
                    this.failureHandler.onAuthenticationFailure(request, response, ex);
                    return;
                }
                //认证通过保存context
                this.securityContextRepository.saveContext(SecurityContextHolder.getContext(), request, response);
            }
            else {
                // No security context or authentication present. Check for a session
                // timeout
                if (request.getRequestedSessionId() != null && !request.isRequestedSessionIdValid()) {
                    if (this.logger.isDebugEnabled()) {
                        this.logger.debug(LogMessage.format("Request requested invalid session id %s",
                                request.getRequestedSessionId()));
                    }
                    if (this.invalidSessionStrategy != null) {
                        this.invalidSessionStrategy.onInvalidSessionDetected(request, response);
                        return;
                    }
                }
            }
        }
        chain.doFilter(request, response);
    }

<1>

org.springframework.security.web.context.HttpSessionSecurityContextRepository#containsContext

初始化处 可参考:https://www.cnblogs.com/LQBlog/p/15529767.html#autoid-0-0-0

    @Override
    public boolean containsContext(HttpServletRequest request) {
        HttpSession session = request.getSession(false);
        if (session == null) {
            return false;
        }
        return session.getAttribute(this.springSecurityContextKey) != null;
    }

<2>

com.biz.soa.order.service.oms.push.builder.omssend.Director#onAuthentication

 public void onAuthentication(Authentication authentication, HttpServletRequest request,
                                 HttpServletResponse response) {
        //最大允许session会话数量
        int allowedSessions = getMaximumSessionsForThisUser(authentication);
        if (allowedSessions == -1) {
            // We permit unlimited logins
            return;
        }
        //根据用户登录名字 查询当前所有会话
        List<SessionInformation> sessions = this.sessionRegistry.getAllSessions(authentication.getPrincipal(), false);
        int sessionCount = sessions.size();
        if (sessionCount < allowedSessions) {
            // They haven't got too many login sessions running at present
            return;
        }
        //如果超过
        if (sessionCount == allowedSessions) {
            HttpSession session = request.getSession(false);
            // 将自己之前的会话都清除
            if (session != null) {
                for (SessionInformation si : sessions) {
                    if (si.getSessionId().equals(session.getId())) {
                        return;
                    }
                }
            }
        }
        //<3>清除会话
        allowableSessionsExceeded(sessions, allowedSessions, this.sessionRegistry);
    }

<3>

org.springframework.security.web.authentication.session.ConcurrentSessionControlAuthenticationStrategy#allowableSessionsExceeded

    protected void allowableSessionsExceeded(List<SessionInformation> sessions, int allowableSessions,
                                             SessionRegistry registry) throws SessionAuthenticationException {
        //这里应该exceptionIfMaximumExceeded 配置了这个是限制登录不超过
        if (this.exceptionIfMaximumExceeded || (sessions == null)) {
            throw new SessionAuthenticationException(
                    this.messages.getMessage("ConcurrentSessionControlAuthenticationStrategy.exceededAllowed",
                            new Object[] { allowableSessions }, "Maximum sessions of {0} for this principal exceeded"));
        }
        // 这里应该是将其他用户挤出去
        sessions.sort(Comparator.comparing(SessionInformation::getLastRequest));
        int maximumSessionsExceededBy = sessions.size() - allowableSessions + 1;
        List<SessionInformation> sessionsToBeExpired = sessions.subList(0, maximumSessionsExceededBy);
        for (SessionInformation session : sessionsToBeExpired) {
            //设置session 过期后续再这个filter就会被自动踢出去<跳转>
            session.expireNow();
        }
    }
原文地址:https://www.cnblogs.com/LQBlog/p/15538887.html