• 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();
            }
        }
  • 相关阅读:
    Nginx负载均衡:分布式/热备Web Server的搭建
    CentOS6.6 32位 Minimal版本纯编译安装Nginx Mysql PHP Memcached
    windows下nginx安装、配置与使用
    Redis基本操作——List
    MongoDB aggregate 运用篇 个人总结
    构建一个较为通用的业务技术架构
    2016年31款轻量高效的开源JavaScript插件和库
    正则表达式
    前端学习路线
    可变参数
  • 原文地址:https://www.cnblogs.com/LQBlog/p/15538887.html
Copyright © 2020-2023  润新知