对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(); } }