Spring Security 认证流程分析,带你Debugger

本文深入剖析Spring Security的默认表单认证流程,从启动项目、发起POST登录请求开始,逐步分析AbstractAuthenticationProcessingFilter、UsernamePasswordAuthenticationFilter、AuthenticationManager、ProviderManager和AuthenticationProvider等关键组件的作用,揭示Spring Security认证过程的细节。
摘要由CSDN通过智能技术生成

前言

对 Spring Security 的文章也写了好几篇了,有基本使用的,有对其进行自定义逻辑的。但那时各位肯定不知道为什么需要这么去定义。所以,本篇呢,来理一理 Spring Security 中默认的表单认证流程源码!

依赖版本

名称 版本
spring-boot-starter-parent 2.3.12.RELEASE
spring-boot-starter-security 2.3.12.RELEASE
spring-security-web 5.3.9.RELEASE

调试开始

就加入 Spring Security 依赖后,启动项目,访问登录页面,在页面输入默认的用户名、密码后,点击提交按钮,会发起一个请求类型为 POST,请求路径为 /login 的接口。 该请求会被 Spring Security 的过滤器链代理拦截。

该请求会来到 UsernamePasswordAuthenticationFilter

UsernamePasswordAuthenticationFilter 这个对象是怎么来的呢? 我们可以看看 WebSecurityConfigurerAdapter 抽象类中的 configure(HttpSecurity http) 方法中提供了一个默认的配置,代码如下:

protected void configure(HttpSecurity http) throws Exception {
    logger.debug("Using default configure(HttpSecurity). If subclassed this will potentially override subclass configure(HttpSecurity).");
​
    http
        .authorizeRequests()
            .anyRequest().authenticated()
            .and()
        .formLogin().and()
        .httpBasic();
}
复制代码
  • authorizeRequests:允许使用 RequestMatcher 实现(即通过URL模式)基于 HttpServletRequest 限制请求访问。
  • anyRequest().authenticated():请求都需要被认证
  • formLogin:指定支持基于表单的身份验证。如果 FormLoginConfigurer未指定 loginPage,将生成默认登录页。
  • httpBasic:可以配置basic登录

进入 formLogin 方法。

public FormLoginConfigurer<HttpSecurity> formLogin() throws Exception {
   return getOrApply(new FormLoginConfigurer<>());
}
复制代码

可以看到这里创建了一个 FormLoginConfigurer对象,FormLoginConfigure是一个用户名密码表单登录的配置类,比如设置登录页面的Url,登录请求的Url 、登录认证成功、认证失败的回调、设置登录请求参数名等等。

public FormLoginConfigurer() {
   super(new UsernamePasswordAuthenticationFilter(), null);
   usernameParameter("username");
   passwordParameter("password");
}
复制代码

在其无参构造方法中,创建了一个 UsernamePasswordAuthenticationFilter 对象。将其传递给父类,父类会将该过滤器添加到过滤器链中。

AbstractAuthenticationProcessingFilter

上面说到请求会来到 UsernamePasswordAuthenticationFilter,但是它继承了 AbstractAuthenticationProcessingFilter,所以请求会先进入 AbstractAuthenticationProcessingFilter抽象类中,它是一个 Filter,那我们将断点打在 doFilter 方法中。

介绍一下 chain 中的属性,originalChain 表示原生的过滤器链,也就是 Web Filter;additionalFilters 表示 Spring Security 中的过滤器链;firewalledRequest 表示当前请求;size 表示过滤器链中过滤器的个数;currentPosition 则是过滤器链遍历时候的下标。

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

   HttpServletRequest request = (HttpServletRequest) req;
   HttpServletResponse response = (HttpServletResponse) res;
   // 进行请求过滤,判断当前调用的请求是否有适配的 xxxAuthenticationFilter 处理
   // 因为 AbstractAuthenticationProcessingFilter 是一个过滤器,那就代表所有的接口请求都会进入该类的 doFilter 方法,
   // 但又不是所有接口都是需要进行登录认证流程的,所以提前进行请求匹配过滤
   if (!requiresAuthentication(request, response)) {
      chain.doFilter(request, response);

      return;
   }

   if (logger.isDebugEnabled()) {
      logger.debug("Request is to process authentication");
   }

  // 身份验证对象
   Authentication authResult;

   try {
     // 调用子类具体实现的 attemptAuthentication(尝试身份验证) 方法
      authResult = attemptAuthentication(request, response);
      if (authResult == null) {
         return;
      }
      // 认证成功时,session 会话需要执行的逻辑
      sessionStrategy.onAuthentication(authResult, request, response);
   }
   catch (InternalAuthenticationServiceException failed) {
   	 // 身份验证失败的默认处理
   	 // 1、清除 SecurityContextHolder
	 // 2、通知配置的RememberMeServices登录失败
	 // 3、将其他行为委托给 AuthenticationFailureHandler,调用其登录失败回调方法(实际开发中会重写AuthenticationFailureHandler接口)
      unsuccessfulAuthentication(request, response, failed);
      return;
   }
   catch (AuthenticationException failed) {
      // Authentication failed
      unsuccessfulAuthentication(request, response, failed);
      return;
   }

   // 认证成功
   // 在successfulAuthentication执行前继续执行过滤器(默认为 false)
   if (continueChainBeforeSuccessfulAuthentication) {
      chain.doFilter(request, response);
   }
 	// 身份验证成功的处理
    // 1、在Sec
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值