首页 > 其他分享 >SpringSecurity过滤器之SessionManagementFilter

SpringSecurity过滤器之SessionManagementFilter

时间:2023-04-30 14:56:57浏览次数:56  
标签:sessions SessionManagementFilter request SpringSecurity authentication session 

SessionManagementFilter检测用户自请求开始以来是否已通过身份验证,如果已通过,则调用SessionAuthenticationStrategy以执行任何与会话相关的活动,例如激活会话固定保护机制或检查多个并发登录。配置如下:

@Configuration
public class MySecurityConfig extends WebSecurityConfigurerAdapter {


    @Override
    protected void configure(HttpSecurity http) throws Exception {
        http.authorizeRequests()
                .anyRequest()
                .authenticated()
                .and()
                .formLogin()
                .permitAll()
                .and()
                .sessionManagement()
                .sessionFixation()
                .newSession()
                .maximumSessions(1);
    }
}

sessionFixation是配置会话固定保护策略的。maximumSessions配置session最大并发数。

 
 

源码解析

SessionManagementFilter

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);
	if (!this.securityContextRepository.containsContext(request)) {
		Authentication authentication = SecurityContextHolder.getContext().getAuthentication();
		if (authentication != null && !this.trustResolver.isAnonymous(authentication)) {
			// The user has been authenticated during the current request, so call the
			// session strategy
			try {
				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();
				this.failureHandler.onAuthenticationFailure(request, response, ex);
				return;
			}
			// Eagerly save the security context to make it available for any possible
			// re-entrant requests which may occur before the current request
			// completes. SEC-1396.
			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、通过request.getAttribute(FILTER_APPLIED)判断请求是否已经通过身份验证,如果通过则放行否则进行下一步。
2、通过this.securityContextRepository.containsContext(request)判断当前请求是否包含security context(默认是判断session是否已有SPRING_SECURITY_CONTEXT属性,在保存security context时会设置SPRING_SECURITY_CONTEXT属性)。如果已经包含则放行。否则进行下一步。
3、从SecurityContextHolder中获取Authentication(身份鉴权对象),Authentication存在且不是匿名用户调用sessionAuthenticationStrategy.onAuthentication执行任何与会话相关的活动,例如激活会话固定保护机制或检查多个并发登录。failureHandler.onAuthenticationFailure处理异常。
4、如果Authentication不存在或是匿名用户则invalidSessionStrategy.onInvalidSessionDetected处理session过期。

 
 

并发session由ConcurrentSessionControlAuthenticationStrategy处理。

ConcurrentSessionControlAuthenticationStrategy#onAuthentication(Authentication authentication, HttpServletRequest request,HttpServletResponse response)

public void onAuthentication(Authentication authentication, HttpServletRequest request,
		HttpServletResponse response) {
	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) {
			// Only permit it though if this request is associated with one of the
			// already registered sessions
			for (SessionInformation si : sessions) {
				if (si.getSessionId().equals(session.getId())) {
					return;
				}
			}
		}
		// If the session is null, a new one will be created by the parent class,
		// exceeding the allowed number
	}
	allowableSessionsExceeded(sessions, allowedSessions, this.sessionRegistry);
}

1、getMaximumSessionsForThisUser通过鉴权用户获取最大session并发数,就是上面配置类设置的maximumSessions。如果不限制session并发数,即maximumSessions等于-1.返回。
2、sessionRegistry.getAllSessions通过Principal(用户对象)获取所有的session。实际是ConcurrentMap保存了每个登录用户的sessionId。登录用户作为key,如果自定义登录用户对象就必须要实现equals和hashCode方法。
3、如果用户的登录session数等于最大并发数,则判断在登录的session里面是否存在本次登录的session,如果存在则返回。否则调用allowableSessionsExceeded处理并发session问题。

protected void allowableSessionsExceeded(List<SessionInformation> sessions, int allowableSessions,
		SessionRegistry registry) throws SessionAuthenticationException {
	if (this.exceptionIfMaximumExceeded || (sessions == null)) {
		throw new SessionAuthenticationException(
				this.messages.getMessage("ConcurrentSessionControlAuthenticationStrategy.exceededAllowed",
						new Object[] { allowableSessions }, "Maximum sessions of {0} for this principal exceeded"));
	}
	// Determine least recently used sessions, and mark them for invalidation
	sessions.sort(Comparator.comparing(SessionInformation::getLastRequest));
	int maximumSessionsExceededBy = sessions.size() - allowableSessions + 1;
	List<SessionInformation> sessionsToBeExpired = sessions.subList(0, maximumSessionsExceededBy);
	for (SessionInformation session : sessionsToBeExpired) {
		session.expireNow();
	}
}

将session按照lastRequest排序后将多出来的session调用expireNow设置为过期。

固定session保护

会话固定攻击参考什么是会话固定攻击?Spring Boot 中要如何防御会话固定攻击? 。固定session保护策略由sessionFixation()配置,有四个选项:

  • newSession():每个请求创建新会话,但不应保留原始HttpSession中的会话属性。
  • changeSessionId():session不变,使用HttpServlet Request.changeSessionId()防止会话固定攻击,默认实现
  • migrateSession():每个请求创建新会话,并将原来的session属性复制到新的session中。
  • none():不开启会话固定保护。

标签:sessions,SessionManagementFilter,request,SpringSecurity,authentication,session,
From: https://www.cnblogs.com/shigongp/p/17365113.html

相关文章