微人事-session并发问题-踢掉已登录的用户(1)

前言

spring security的登录逻辑是在UsernamePasswordAuthenticationFilter#attemptAuthentication方法中实现的,但是attemptAuthentication的调用者却是UsernamePasswordAuthenticationFilter的父类AbstractAuthenticationProcessingFilter间接调用的,源码如下

public void doFilter(ServletRequest req, ServletResponse res, FilterChain chain)
			throws IOException, ServletException {

		HttpServletRequest request = (HttpServletRequest) req;
		HttpServletResponse response = (HttpServletResponse) res;

		if (!requiresAuthentication(request, response)) {
			chain.doFilter(request, response);

			return;
		}
		Authentication authResult;

		try {
		    //这里就是调用UsernamePasswordAuthenticationFilter#attemptAuthentication
			authResult = attemptAuthentication(request, response);
			if (authResult == null) {
				// return immediately as subclass has indicated that it hasn't completed
				// authentication
				return;
			}
			//这里就是今天的重点,session并发管理
			sessionStrategy.onAuthentication(authResult, request, response);
		}
		
		// Authentication success
		if (continueChainBeforeSuccessfulAuthentication) {
			chain.doFilter(request, response);
		}

		successfulAuthentication(request, response, chain, authResult);
	}
分析
sessionStrategy.onAuthentication(authResult, request, response);

(1)首先通过源码分析,这里的sessionStrategy是一个SessionAuthenticationStrategy接口对象,onAuthentication调用的是此接口的一个实现类ConcurrentSessionControlAuthenticationStrategy,
下面具体看下ConcurrentSessionControlAuthenticationStrategy#onAuthentication

public void onAuthentication(Authentication authentication,
			HttpServletRequest request, HttpServletResponse response) {
		/*
		authentication.getPrincipal()获取当前登录的用户对象,以此对象为key,去找对应的value,
		这里的value就是此用户所对应的session集合
		*/
		final List<SessionInformation> sessions = sessionRegistry.getAllSessions(
				authentication.getPrincipal(), false);
		
		int sessionCount = sessions.size();
		//获取允许的 session 并发数
		int allowedSessions = getMaximumSessionsForThisUser(authentication);
		/*下面三个if,比较当前session与allowedSessions
		*/ 
		if (sessionCount < allowedSessions) {
			// They haven't got too many login sessions running at present
			return;
		}
		
		if (allowedSessions == -1) {
			// We permit unlimited logins
			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
		}
		//若当前session数大于所限制的session数
		allowableSessionsExceeded(sessions, allowedSessions, sessionRegistry);
	}

  1. 如果当前 session 数(sessionCount)小于 session 并发数(allowedSessions),则不做任何处理;如果 allowedSessions 的值为 -1,表示对 session 数量不做任何限制。
  2. 如果当前 session 数(sessionCount)等于 session 并发数(allowedSessions),那就先看看当前 session 是否不为 null,并且已经存在于 sessions 中了,如果已经存在了,那都是自家人,不做任何处理;如果当前 session 为 null,那么意味着将有一个新的 session 被创建出来,届时当前 session 数(sessionCount)就会超过 session 并发数(allowedSessions)。
  3. 如果前面的代码中都没能 return 掉,那么将进入策略判断方法 allowableSessionsExceeded 中。

(2)#allowableSessionsExceeded源码如下

protected void allowableSessionsExceeded(List<SessionInformation> sessions,
			int allowableSessions, SessionRegistry registry)
			throws SessionAuthenticationException {
			//exceptionIfMaximumExceeded  表示是否禁止新的登录 
			//对应SecurityConfig 中配置的 maxSessionsPreventsLogin 的值
		if (exceptionIfMaximumExceeded || (sessions == null)) {
			throw new SessionAuthenticationException(messages.getMessage(
					"ConcurrentSessionControlAuthenticationStrategy.exceededAllowed",
					new Object[] { Integer.valueOf(allowableSessions) },
					"Maximum sessions of {0} for this principal exceeded"));
		}

		// 通过遍历session集合方式找出最早登录的session,并将其致为过期session
		SessionInformation leastRecentlyUsed = null;

		for (SessionInformation session : sessions) {
			if ((leastRecentlyUsed == null)
					|| session.getLastRequest()
							.before(leastRecentlyUsed.getLastRequest())) {
				leastRecentlyUsed = session;
			}
		}

		leastRecentlyUsed.expireNow();
	}
总结

到这我们了解了spring security如何处理session并发的,分为两步1.判断session是否超过所允许的 session 并发数。2.若超过,则将最早登录的session做过期处理。
下一节:

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值