Spring Security认证的完整流程记录

目录
  • 前言
  • 认证上下文的持久化
  • 认证信息的封装
  • 查找处理认证的 Provider 类
  • 认证逻辑
  • 总结

前言

本文以用户名/密码验证方式为例,讲解 Spring Security 的认证流程,在此之前,需要你了解 Spring Security 用户名/密码认证的基本配置。

Spring Security 是基于过滤器的,通过一层一层的过滤器,处理认证的流程,拦截非法请求。

认证上下文的持久化

处于最前面的过滤器叫做 SecurityContextPersistenceFilter,Spring Security 是通过 Session 来存储认证信息的,这个过滤器的 doFilter 方法在每次请求中只执行一次,作用就是,在请求时,将 Session 中的 SecurityContext 放到当前请求的线程中(如果有),在响应时,检查县城中是否有 SecurityContext,有的话将其放入 Session。可以理解为将 SecurityContext 进行 Session 范围的持久化。

认证信息的封装

接着进入 UsernamePasswordAuthenticationFilter,这是基于用户名/密码认证过程中的主角之一。

默认情况下,这个过滤器会匹配路径为 /login 的 POST 请求,也就是 Spring Security 默认的用户名和密码登录的请求路径。

这里最关键的代码是 attemptAuthentication 方法(由 doFilter 方法调用),源码如下:

@Override
public Authentication attemptAuthentication ( HttpServletRequest request, HttpServletResponse response )
 throws AuthenticationException {
 if ( this.postOnly && !request.getMethod () .equals ( "POST" )) {
 throw new AuthenticationServiceException ( "Authentication method not supported: " + request.getMethod ()) ;
   }
 String username = obtainUsername ( request ) ;
   username = ( username != null ) ? username : "";
   username = username.trim () ;
   String password = obtainPassword ( request ) ;
   password = ( password != null ) ? password : "";
   UsernamePasswordAuthenticationToken authRequest = new UsernamePasswordAuthenticationToken ( username, password ) ;
   // Allow subclasses to set the "details" property
   setDetails ( request, authRequest ) ;
   return this.getAuthenticationManager () .authenticate ( authRequest ) ;
 }

attemptAuthentication 方法代码的第 12 行,使用从 request 中获取到的用户名和密码,构建了一个 UsernamePasswordAuthenticationToken 对象,我们可以看到这个构造方法的代码,非常简单:

public UsernamePasswordAuthenticationToken(Object principal, Object credentials) {
    super((Collection)null);
    this.principal = principal;
    this.credentials = credentials;
    this.setAuthenticated(false);
}

只是保存了用户名和密码的引用,并且将认证状态设置为 false,因为此时只是封装了认证信息,还没有进行认证。

我们再回到 attemptAuthentication 的代码,在方法的最后一行,将创建好的认证信息,传递给了一个 AuthenticationManager 进行认证。这里实际工作的是 AuthenticationManager 的实现类 ProviderManager

查找处理认证的 Provider 类

进入 ProviderManager 可以从源码中找到 authenticate 方法,代码比较长,我就不贴在这里了,你可以自行查找,我简述一下代码中的逻辑。

ProviderManager 本身不执行认证操作,它管理着一个 AuthenticationProvider 列表,当需要对一个封装好的认证信息进行认证操作的时候,它会将认证信息和它管理者的 Provider 们,逐一进行匹配,找到合适的 Provider 处理认证的具体工作。

可以这样理解,ProviderManager 是一个管理者,管理着各种各样的 Provider。当有工作要做的时候,它从来都不亲自去做,而是把不同的工作,分配给不同的 Provider 去操作。

最后,它会将 Provider 的工作成果(已认证成功的信息)返回,或者抛出异常。

那么,它是怎么将一个认证信息交给合适的 Provider 的呢?

在上一部分中,我们说到,认证信息被封装成了一个 UsernamePasswordAuthenticationToken,它是Authentication 的子类,ProviderManager 会将这个认证信息的类型,传递个每个 Provider 的 supports 方法,由 Provider 来告诉 ProviderManager 它是不是支持这个类型的认证信息。

认证逻辑

在 Spring Security 内置的 Provider 中,与 UsernamePasswordAuthenticationToken 对应的 Provider 是 DaoAuthenticationProviderauthenticate 方法在它的父类 AbstractUserDetailsAuthenticationProvider 中。我们来看它的 authenticate 方法:

@Override
public Authentication authenticate ( Authentication authentication ) throws AuthenticationException {
 Assert.isInstanceOf( UsernamePasswordAuthenticationToken.class, authentication,
         () -> this.messages.getMessage ( "AbstractUserDetailsAuthenticationProvider.onlySupports",
               "Only UsernamePasswordAuthenticationToken is supported" )) ;
   String username = determineUsername ( authentication ) ;
   boolean cacheWasUsed = true;
   UserDetails user = this.userCache.getUserFromCache ( username ) ;
   if ( user == null ) {
 cacheWasUsed = false;
      try {
 user = retrieveUser ( username, ( UsernamePasswordAuthenticationToken ) authentication ) ;
      }
 catch ( UsernameNotFoundException ex ) {
 this.logger.debug ( "Failed to find user '" + username + "'" ) ;
         if ( !this.hideUserNotFoundExceptions ) {
 throw ex;
         }
 throw new BadCredentialsException ( this.messages
               .getMessage ( "AbstractUserDetailsAuthenticationProvider.badCredentials", "Bad credentials" )) ;
      }
 Assert.notNull( user, "retrieveUser returned null - a violation of the interface contract" ) ;
   }
 try {
 this.preAuthenticationChecks.check ( user ) ;
      additionalAuthenticationChecks ( user, ( UsernamePasswordAuthenticationToken ) authentication ) ;
   }
 catch ( AuthenticationException ex ) {
 if ( !cacheWasUsed ) {
 throw ex;
      }
 // There was a problem, so try again after checking
      // we're using latest data (i.e. not from the cache)
      cacheWasUsed = false;
      user = retrieveUser ( username, ( UsernamePasswordAuthenticationToken ) authentication ) ;
      this.preAuthenticationChecks.check ( user ) ;
      additionalAuthenticationChecks ( user, ( UsernamePasswordAuthenticationToken ) authentication ) ;
   }
 this.postAuthenticationChecks.check ( user ) ;
   if ( !cacheWasUsed ) {
 this.userCache.putUserInCache ( user ) ;
   }
 Object principalToReturn = user;
   if ( this.forcePrincipalAsString ) {
 principalToReturn = user.getUsername () ;
   }
 return createSuccessAuthentication ( principalToReturn, authentication, user ) ;
 }

代码比较长,我们说要点:

  • 代码第 12 行,通过 retrieveUser 方法,获得 UserDetails 信息,这个方法的具体实现,可以在 DaoAuthenticationProvider 中找到,主要是通过 UserDetailsService 的 loadUserByUsername 方法,查找系统中的用户信息。
  • 代码第 25 行,通过 preAuthenticationChecks.check 方法,进行了认证前的一些校验。校验的具体实现可以在 DefaultPreAuthenticationChecks 内部类中找到,主要是判断用户是否锁定、是否可用、是否过期。
  • 代码第 26 行,通过 additionalAuthenticationChecks 方法,对用户名和密码进行了校验。具体实现可以在 DaoAuthenticationProvider 中找到。
  • 代码第 39 行,通过 postAuthenticationChecks.check 方法,校验了密码是否过期。具体实现可以在 DefaultPostAuthenticationChecks 内部类中找到。
  • 最后,如果以上校验和认证都没有问题,则通过 createSuccessAuthentication 方法,创建成功的认证信息,并返回。此时,就成功通过了认证。

在最后的 createSuccessAuthentication 方法中,会创建一个新的 UsernamePasswordAuthenticationToken 认证信息,这个新的认证信息的认证状态为 true。表示这是一个已经通过的认证。

这个认证信息会返回到 UsernamePasswordAuthenticationFilter 中,并作为 attemptAuthentication 方法的结果。

doFilter 方法中,会根据认证成功或失败的结果,调用相应的 Handler 类进行后续的处理,最后,认证的信息也会被保存在 SecurityContext 中,供后续使用。

总结

到此这篇关于Spring Security认证流程的文章就介绍到这了,更多相关Spring Security认证流程内容请搜索我们以前的文章或继续浏览下面的相关文章希望大家以后多多支持我们!

(0)

相关推荐

  • Spring Security使用数据库认证及用户密码加密和解密功能

    流程图: 1.接上一篇博客https://mp.csdn.net/console/editor/html/104576494,准备好环境. 2.spring-security.xml中的配置: <?xml version="1.0" encoding="UTF-8"?> <beans xmlns="http://www.springframework.org/schema/beans" xmlns:security="

  • Spring security基于数据库中账户密码认证

    一.原理分析 前台的登录请求发送到后端后会由spring security进行拦截,即controller层由框架自己提供.这样用户名和密码的认证就需要在service层完成,所以框架需要在service层获取到我们自己的数据库账号信息. spring security 提供了一个接口 UserDetailsService 来让用户提供账号和密码,其内容如下 public interface UserDetailsService { UserDetails loadUserByUsername(

  • 详解Spring Security认证流程

    前言 Spring Seuciry相关的内容看了实在是太多了,但总觉得还是理解地不够巩固,还是需要靠知识输出做巩固. 相关版本: java: jdk 8 spring-boot: 2.1.6.RELEASE 过滤器链和认证过程 一个认证过程,其实就是过滤器链上的一个绿色矩形Filter所要执行的过程. 基本的认证过程有三步骤: Filter拦截请求,生成一个未认证的Authentication,交由AuthenticationManager进行认证: AuthenticationManager的

  • spring security自定义认证登录的全过程记录

    spring security使用分类: 如何使用spring security,相信百度过的都知道,总共有四种用法,从简到深为: 1.不用数据库,全部数据写在配置文件,这个也是官方文档里面的demo: 2.使用数据库,根据spring security默认实现代码设计数据库,也就是说数据库已经固定了,这种方法不灵活,而且那个数据库设计得很简陋,实用性差: 3.spring security和Acegi不同,它不能修改默认filter了,但支持插入filter,所以根据这个,我们可以插入自己的f

  • SpringSecurity认证流程详解

    SpringSecurity基本原理 在之前的文章<SpringBoot + Spring Security 基本使用及个性化登录配置>中对SpringSecurity进行了简单的使用介绍,基本上都是对于接口的介绍以及功能的实现. 这一篇文章尝试从源码的角度来上对用户认证流程做一个简单的分析. 在具体分析之前,我们可以先看看SpringSecurity的大概原理: SpringSecurity基本原理 其实比较简单,主要是通过一系列的Filter对请求进行拦截处理. 认证处理流程说明 我们直接

  • Spring Security认证的完整流程记录

    目录 前言 认证上下文的持久化 认证信息的封装 查找处理认证的 Provider 类 认证逻辑 总结 前言 本文以用户名/密码验证方式为例,讲解 Spring Security 的认证流程,在此之前,需要你了解 Spring Security 用户名/密码认证的基本配置. Spring Security 是基于过滤器的,通过一层一层的过滤器,处理认证的流程,拦截非法请求. 认证上下文的持久化 处于最前面的过滤器叫做 SecurityContextPersistenceFilter,Spring

  • Spring Security认证器实现过程详解

    目录 拦截请求 验证过程 返回完整的Authentication 收尾工作 结论 一些权限框架一般都包含认证器和决策器,前者处理登陆验证,后者处理访问资源的控制 Spring Security的登陆请求处理如图 下面来分析一下是怎么实现认证器的 拦截请求 首先登陆请求会被UsernamePasswordAuthenticationFilter拦截,这个过滤器看名字就知道是一个拦截用户名密码的拦截器 主要的验证是在attemptAuthentication()方法里,他会去获取在请求中的用户名密码

  • spring security认证异常后返回中文提示的问题

    1.加载中文提示类 @Configuration public class ReloadMessageConfig { /** * 加载中文的认证提示信息 * * @return */ @Bean public ReloadableResourceBundleMessageSource messageSource() { ReloadableResourceBundleMessageSource messageSource = new ReloadableResourceBundleMessag

  • Java Spring Security认证与授权及注销和权限控制篇综合解析

    Spring Security简介: Spring Security 是针对Spring项目的安全框架,也是Spring Boot底层安全模块默认的技术选型,它可以实现强大的Web安全控制,对于安全控制,我们只需要引入 spring-boot-starter-security 模块,进行少量的配置,即可实现强大的安全管理! 记住几个类: WebSecurityConfigurerAdapter:自定义Security策略 AuthenticationManagerBuilder:自定义认证策略

  • Spring Security认证提供程序示例详解

    1.简介 本教程将介绍如何在Spring Security中设置身份验证提供程序,与使用简单UserDetailsService的标准方案相比,提供了额外的灵活性. 2. The Authentication Provider Spring Security提供了多种执行身份验证的选项 - 所有这些都遵循简单的规范 - 身份验证请求由Authentication Provider处理,并且返回具有完整凭据的完全身份验证的对象. 标准和最常见的实现是DaoAuthenticationProvide

  • Spring Security 自定义授权服务器实践记录

    目录 前言 授权服务器变迁 最小化配置 安装授权服务器 配置授权服务器 配置客户端 体验 总结 前言 在之前我们已经对接过了GitHub.Gitee客户端,使用OAuth2 Client能够快速便捷的集成第三方登录,集成第三方登录一方面降低了企业的获客成本,同时为用户提供更为便捷的登录体验.但是随着企业的发展壮大,越来越有必要搭建自己的OAuth2服务器.OAuth2不仅包括前面的OAuth客户端,还包括了授权服务器,在这里我们要通过最小化配置搭建自己的授权服务器.授权服务器主要提供OAuth

  • spring Bean创建的完整过程记录

    目录 前言 bean创建的流程图 快速开始 总结 前言 复习一下spring实现IOC的源码流程准备工作: ​强烈建议大家从git上拉取spring源码来学习Spring源码.因为里面相较于IDEA生成的会有注释,里面有的方法会有注释看起来会省力一点. ​以下都是用5.0.2版本来做阐述. bean创建的流程图 写在前面:建议大家一定要自己用实例跑一遍,做好记录.如果只是看看会非常抽象.此流程图作为梗概,便于加强记忆和理解,新手或无基础的有个印象即可.等跟随本文走通一遍,在回过头看这个图,或许会

  • Spring Security整合Oauth2实现流程详解

    一.创建项目并导入依赖 <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-security</artifactId> </dependency> <dependency> <groupId>org.springframework.boot</groupId> <art

  • Spring security认证两类用户代码实例

    向容器中添加一个自定义的认证类实现UserDetailsService 在这个类里面就可以进行用户类型的判断,包括三类用户(管理员使用内存认证,老师和学生都使用数据库认证) 然后在 然后在configure里面设置认证的类和密码的加密方式 以上就是本文的全部内容,希望对大家的学习有所帮助,也希望大家多多支持我们.

随机推荐