跳至主要內容

AbstractAuthenticationProcessingFilter

Jin大约 6 分钟

AbstractAuthenticationProcessingFilter

AbstractAuthenticationProcessingFilterSpring Security 中用于处理认证请求的一个非常重要的抽象类,它是大多数认证相关过滤器(例如,UsernamePasswordAuthenticationFilter)的基类。这个过滤器的主要任务是接收认证请求,提取认证信息,交给 AuthenticationManager 进行认证,并在认证成功或失败时执行后续的处理。它是 Spring Security 中认证机制的核心组成部分。源码路径如下:

org.springframework.security.web.authentication.AbstractAuthenticationProcessingFilter

在 Spring Security 6.x 中,AbstractAuthenticationProcessingFilter 仍然是一个核心的抽象过滤器类,负责处理用户认证请求。与 Spring Security 5.x 版本相比,Spring Security 6.x 进行了许多更新和改进,但 AbstractAuthenticationProcessingFilter 的基本功能和使用方式仍然保持一致。它通常作为身份验证过滤器的基类,用于处理表单登录、基于 HTTP 头的身份验证等多种认证机制。

3.1、概述

AbstractAuthenticationProcessingFilterorg.springframework.security.web.authentication.AbstractAuthenticationProcessingFilter 的完整类名,它是 Spring Security 中用来处理认证请求的抽象类。这个过滤器负责拦截用户提交的认证请求(如表单登录请求),并从请求中提取认证信息,然后将这些信息交给 AuthenticationManager 进行认证。

Spring Security 6.x 中,这个类的核心功能并未发生太大变化,主要用于:

  • 拦截认证请求;
  • 提取认证信息;
  • 调用 AuthenticationManager 进行认证;
  • 处理认证成功和失败的后续逻辑。

2、主要功能和工作流程

  1. 拦截认证请求:它会根据配置的 URL 模式拦截指定的请求路径。通常,这些请求会是提交登录表单的 POST 请求。
  2. 提取认证信息:该过滤器会从请求中提取用户认证信息(如用户名、密码等)。这些信息会被封装到 Authentication 对象中,常见的如 UsernamePasswordAuthenticationToken
  3. 认证管理器认证:认证信息会被传递给 AuthenticationManager 进行验证,AuthenticationManager 会通过配置的 AuthenticationProvider 进行认证。
  4. 认证结果处理:根据认证的结果:
    • 成功:认证通过后,用户信息会被存储到 SecurityContext 中,使得后续的请求可以通过认证。
    • 失败:认证失败时,可以配置不同的失败处理机制(如重定向到登录页面、返回错误信息等)

3、关键方法详解

AbstractAuthenticationProcessingFilter 继承自 GenericFilterBean,并实现了 Spring Security 的过滤器功能。其核心方法包括

3.1 doFilter(HttpServletRequest request, HttpServletResponse response, FilterChain chain)

  • 作用:这个方法是过滤器链中的核心,负责判断请求是否需要认证,并将请求交给认证管理器进行认证。
  • 工作流程:
    • 它首先通过 requiresAuthentication(request, response) 判断请求是否为需要认证的请求。
    • 如果需要认证,它会调用 attemptAuthentication(request, response) 从请求中提取认证信息并尝试进行认证。
    • 如果认证成功,它会把认证信息存储到 SecurityContext 中;如果认证失败,则调用失败处理方法 unsuccessfulAuthentication(request, response, failed)
	private void doFilter(HttpServletRequest request, HttpServletResponse response, FilterChain chain)
			throws IOException, ServletException {
		if (!requiresAuthentication(request, response)) {
			chain.doFilter(request, response);
			return;
		}
		try {
			Authentication authenticationResult = attemptAuthentication(request, response);
			if (authenticationResult == null) {
				// return immediately as subclass has indicated that it hasn't completed
				return;
			}
			this.sessionStrategy.onAuthentication(authenticationResult, request, response);
			// Authentication success
			if (this.continueChainBeforeSuccessfulAuthentication) {
				chain.doFilter(request, response);
			}
			successfulAuthentication(request, response, chain, authenticationResult);
		}
		catch (InternalAuthenticationServiceException failed) {
			this.logger.error("An internal error occurred while trying to authenticate the user.", failed);
			unsuccessfulAuthentication(request, response, failed);
		}
		catch (AuthenticationException ex) {
			// Authentication failed
			unsuccessfulAuthentication(request, response, ex);
		}
	}

3.2 requiresAuthentication(HttpServletRequest request, HttpServletResponse response)

  • 作用:用于判断当前请求是否需要认证,通常基于请求的 URL 或请求方法(如 POSTGET)进行判断。
  • 默认实现:通常,AbstractAuthenticationProcessingFilter 会通过 URL 匹配来判断是否需要认证。例如,UsernamePasswordAuthenticationFilter 会匹配登录请求 URL。
	protected boolean requiresAuthentication(HttpServletRequest request, HttpServletResponse response) {
		if (this.requiresAuthenticationRequestMatcher.matches(request)) {
			return true;
		}
		if (this.logger.isTraceEnabled()) {
			this.logger
				.trace(LogMessage.format("Did not match request to %s", this.requiresAuthenticationRequestMatcher));
		}
		return false;
	}

3.3 attemptAuthentication(HttpServletRequest request, HttpServletResponse response)

  • 作用:从请求中提取认证信息并将其封装为 Authentication 对象。通常,子类会重写此方法,根据具体的认证方式(如表单登录、OAuth 认证等)提取认证信息并创建相应的 Authentication 对象。

    例如,在 UsernamePasswordAuthenticationFilter 中,attemptAuthentication 从请求中提取用户名和密码,封装成 UsernamePasswordAuthenticationToken,然后交给 AuthenticationManager 进行认证。

	public abstract Authentication attemptAuthentication(HttpServletRequest request, HttpServletResponse response)
			throws AuthenticationException, IOException, ServletException;

3.4、 successfulAuthentication(HttpServletRequest request, HttpServletResponse response, FilterChain chain, Authentication authResult)

  1. 作用:认证成功时调用。通常在这里可以将认证结果存储到 SecurityContext 中,允许后续的请求访问认证信息。
  2. 默认行为:在认证成功后,SecurityContextHolder.getContext().setAuthentication(authResult) 会将认证信息存储到 SecurityContext 中。
	protected void successfulAuthentication(HttpServletRequest request, HttpServletResponse response, FilterChain chain,
			Authentication authResult) throws IOException, ServletException {
		SecurityContext context = this.securityContextHolderStrategy.createEmptyContext();
		context.setAuthentication(authResult);
		this.securityContextHolderStrategy.setContext(context);
		this.securityContextRepository.saveContext(context, request, response);
		if (this.logger.isDebugEnabled()) {
			this.logger.debug(LogMessage.format("Set SecurityContextHolder to %s", authResult));
		}
		this.rememberMeServices.loginSuccess(request, response, authResult);
		if (this.eventPublisher != null) {
			this.eventPublisher.publishEvent(new InteractiveAuthenticationSuccessEvent(authResult, this.getClass()));
		}
		this.successHandler.onAuthenticationSuccess(request, response, authResult);
	}

3.5、 unsuccessfulAuthentication(HttpServletRequest request, HttpServletResponse response, AuthenticationException failed)

  1. 作用:认证失败时调用。可以在这里处理认证失败后的逻辑,例如跳转到登录失败页面、返回错误信息等。
  2. 默认行为:清除当前的 SecurityContext,然后根据配置的 AuthenticationFailureHandler 来处理失败的情况。
	protected void unsuccessfulAuthentication(HttpServletRequest request, HttpServletResponse response,
			AuthenticationException failed) throws IOException, ServletException {
		this.securityContextHolderStrategy.clearContext();
		this.logger.trace("Failed to process authentication request", failed);
		this.logger.trace("Cleared SecurityContextHolder");
		this.logger.trace("Handling authentication failure");
		this.rememberMeServices.loginFail(request, response);
		this.failureHandler.onAuthenticationFailure(request, response, failed);
	}

4、运行流程

  1. 请求处理:当用户发送一个请求时,该请求会被发送到 AbstractAuthenticationProcessingFilter
  2. 尝试认证
    • AbstractAuthenticationProcessingFilterdoFilter方法会调用其子类的attemptAuthentication方法。
    • attemptAuthentication方法中,首先会获取请求参数,通常是用户名(username)和密码(password)。
    • 接着,会创建一个UsernamePasswordAuthenticationToken对象,该对象保存了登录请求的参数,并且标记为未登录认证。
  3. 调用认证方法
    • attemptAuthentication 方法会调用 AuthenticationManagerauthenticate 方法,将UsernamePasswordAuthenticationToken对象作为参数传递。
    • AuthenticationManager的实现类是ProviderManager,它负责选择合适的AuthenticationProvider进行认证。
  4. 认证过程
    • ProviderManager会遍历所有的AuthenticationProvider,找到能够处理UsernamePasswordAuthenticationTokenAuthenticationProvider,通常是DaoAuthenticationProvider
    • DaoAuthenticationProvider会调用UserDetailsServiceloadUserByUsername方法,根据用户名查询用户信息。
    • 接着,DaoAuthenticationProvider会进行前置检查(如用户是否已锁定、不可用、已过期)和密码检查(使用PasswordEncodermatches方法判断密码是否正确)。
    • 如果所有检查都通过,则认证成功,ProviderManager会创建一个新的UsernamePasswordAuthenticationToken对象,表示认证成功,并包含用户的身份、凭证和权限列表。
  5. 处理认证结果
    • 如果认证成功,ProviderManager会隐藏Authentication对象中的密码(数据脱敏),然后返回隐藏密码后的Authentication对象。
    • UsernamePasswordAuthenticationFilterattemptAuthentication方法执行结束后,会将结果交给父类的doFilter方法。
    • doFilter方法会根据登录结果决定运行successfulAuthenticationunsuccessfulAuthentication
      • 如果认证成功,会调用AuthenticationSuccessHandler处理认证成功后的操作,如重定向用户或生成自定义响应。
      • 如果认证失败,会调用AuthenticationFailureHandler处理认证失败的情况,如生成适当的错误响应或执行自定义操作。

5、常见子类

AbstractAuthenticationProcessingFilter 是一个抽象类,通常会被具体的认证过滤器继承和扩展。以下是一些常见的子类:

  1. UsernamePasswordAuthenticationFilter
    • 用于处理传统的基于用户名和密码的表单认证。它会从 HTTP 请求的参数中提取用户名和密码,并将其封装为 UsernamePasswordAuthenticationToken,然后交给 AuthenticationManager 进行认证。
  2. AbstractPreAuthenticatedProcessingFilter
    • 用于预认证模式(pre-authentication)。在此模式下,认证信息(如用户身份)已经包含在请求头中或其他地方。此过滤器会从请求中提取认证信息并进行认证。
  3. OAuth2LoginAuthenticationFilter
    • 用于 OAuth 2.0 登录流程,处理 OAuth2 登录请求,拦截并解析授权码等信息,执行 OAuth2 认证。
贡献者: Jin