Spring security 4.1 登录成功后重复进行认证问题

时间:2022-11-04 18:28:12

问题场景:

登录成功后,在执行某个功能操作(例如:系统管理模块的删除功能时),会去执行UserDetailsService.loadUserByUsername 再次进行用户认证。

出现问题版本 Spring security 4.04  、 4.10


通过源码分析发现BasicAuthenticationFilter.authenticationIsRequired(username) 一直返回true (true表示需要认证)

org.springframework.security.web.authentication.www.BasicAuthenticationFilter.class


关键代码分析

@Override
	protected void doFilterInternal(HttpServletRequest request,
			HttpServletResponse response, FilterChain chain)
					throws IOException, ServletException {
		...
		try {
			...

			if (<span style="color:#ff0000;"><strong>authenticationIsRequired(username)</strong></span>) {
				UsernamePasswordAuthenticationToken authRequest = new UsernamePasswordAuthenticationToken(
						username, tokens[1]);
				authRequest.setDetails(
						this.authenticationDetailsSource.buildDetails(request));
				Authentication authResult = this.authenticationManager
						.authenticate(authRequest);

				if (debug) {
					this.logger.debug("Authentication success: " + authResult);
				}

				SecurityContextHolder.getContext().setAuthentication(authResult);

				this.rememberMeServices.loginSuccess(request, response, authResult);

				onSuccessfulAuthentication(request, response, authResult);
			}

		}
		catch (AuthenticationException failed) {
			...
		}

		chain.doFilter(request, response);
	}

进一步分析authenticationIsRequired(String username),发现问题在existingAuth.getName().equals(username) 每次返回false,导致每次认证不通过。


private boolean authenticationIsRequired(String username) {
		// Only reauthenticate if username doesn't match SecurityContextHolder and user
		// isn't authenticated
		// (see SEC-53)
		Authentication existingAuth = SecurityContextHolder.getContext()
				.getAuthentication();

		if (existingAuth == null || !existingAuth.isAuthenticated()) {
			return true;
		}

		// Limit username comparison to providers which use usernames (ie
		// UsernamePasswordAuthenticationToken)
		// (see SEC-348)

		if (existingAuth instanceof UsernamePasswordAuthenticationToken
				&& !<strong><span style="color:#ff0000;">existingAuth.getName().equals(username)</span></strong>) {
			return true;
		}

		// Handle unusual condition where an AnonymousAuthenticationToken is already
		// present
		// This shouldn't happen very often, as BasicProcessingFitler is meant to be
		// earlier in the filter
		// chain than AnonymousAuthenticationFilter. Nevertheless, presence of both an
		// AnonymousAuthenticationToken
		// together with a BASIC authentication request header should indicate
		// reauthentication using the
		// BASIC protocol is desirable. This behaviour is also consistent with that
		// provided by form and digest,
		// both of which force re-authentication if the respective header is detected (and
		// in doing so replace
		// any existing AnonymousAuthenticationToken). See SEC-610.
		if (existingAuth instanceof AnonymousAuthenticationToken) {
			return true;
		}

		return false;
   }

通过调试后发现,我原先登录账号是大写和数据库一致,第一次登录时账号是大写没有改变,但到了第二次认证后却自动转为小写,所以导致验证不通过。