⭐⭐⭐ Spring Boot 项目实战 ⭐⭐⭐ Spring Cloud 项目实战
《Dubbo 实现原理与源码解析 —— 精品合集》 《Netty 实现原理与源码解析 —— 精品合集》
《Spring 实现原理与源码解析 —— 精品合集》 《MyBatis 实现原理与源码解析 —— 精品合集》
《Spring MVC 实现原理与源码解析 —— 精品合集》 《数据库实体设计合集》
《Spring Boot 实现原理与源码解析 —— 精品合集》 《Java 面试题 + Java 学习指南》

摘要: 原创出处 juejin.cn/post/7329144188926967860 「豫州牧」欢迎转载,保留摘要,谢谢!


🙂🙂🙂关注微信公众号:【芋道源码】有福利:

  1. RocketMQ / MyCAT / Sharding-JDBC 所有源码分析文章列表
  2. RocketMQ / MyCAT / Sharding-JDBC 中文注释源码 GitHub 地址
  3. 您对于源码的疑问每条留言都将得到认真回复。甚至不知道如何读源码也可以请教噢。
  4. 新的源码解析文章实时收到通知。每周更新一篇左右。
  5. 认真的源码交流微信群。

1 前言

在项目开发过程中,会涉及到安全框架的配置。其中常用的就是 shiro 和 spring-security,在本文中将介绍 spring-security 的工作流程和实践应用,并基于此总结其使用心得和项目配置关键。

2 spring security 认证和权限流程

如上图所示,一个请求在达到控制器之前,会经过一系列的过滤器 DefaultSecurityFilterChain,绿色的部分负责用户认证,蓝色的部分处理认证异常的情况,橙色的部分负责用户的授权,最终请求到控制器的方法。在认证完成之后,会将用户认证的结果 Authentication 放进 SecurityContextHolder 中,从中可以获取到用户信息。

在开始之前,需要先介绍一下其他重要的组件

  • SecurityContextHolder: 存放身份信息的容器
  • Authentication: 用户信息的抽象即 security 的认证主体
  • AbstractAuthenticationToken: authentication 即认证主体的抽象类,
  • AuthenticationManager: 身份认证器接口,具体实现类为 ProviderManager
  • AuthenticationProvider: 用来处理 authentication 的认证信息
  • AuthenticationSuccessHandler 认证成功处理器
  • AuthenticationFailureHandler 认证失败处理器

认证的流程如下所示:

1、用户请求的接口经过 security 过滤器,按照其登录方式适配的 authentication 封装其认证方法,通过 AuthenticationProvider 来进行处理认证。获取身份信息会按照其配置provider的顺序来处理。

2、通常情况下将身份信息封装到封装成Authentication下的实现类UsernamePasswordAuthenticationToken, 通常是用户密码的登录方式。

3、通过 AuthenticationManager 身份管理器,通过其配置的provider 验证这个UsernamePasswordAuthenticationToken 的信息。

4、认证逻辑一般是在 service 中,认证成功之后会返回认证信息,AuthenticationManager.authentication身份管理器返回一个Authentication实例(上下文中包含权限信息,身份信息,细节信息,但是密码会被移除)。

5、SecurityContextHolder 上下文容器会填充UsernamePasswordAuthenticationToken信息,通过 SecurityContextHolder.getContext().setAuthentication()方法可以查看其上下文用户信息。

上述流程具体来说,如下图所示:

3 spring security 登录配置

通常情况下,都是用户名和密码表单的方式进行登录,同时在登录时还会添加动态验证码和自动登录的方式,具体如下图所示:

核心配置如下所示:

1、拦截认证请求配置,这里需要配置不需要登录就可以访问的资源,包括登录页面、请求提交页面,静态资源等内容。

  • 登录请求不拦截 permitAll
  • 资源需要某些角色 hasRole
  • 资源访问需要某些权限 hasAuthority
  • 其它的资源需要经过认证,在资源权限上的配置也可以通过注解的方式在控制器中设置

2、登录页面配置,需要配置登录请求的参数,登录页面,登录访问路径以及成功和失败的处理器,这里需要注意的是,successForwardUrl 是请求转发的方式,defaultSuccessUrl是重定向的方式。

3、退出的配置,这里需要配置的是退出的地址以及session 和 authentication 的处理。

4、session 的管理,通常情况下在前后端不分离的情况下,需要用 session 进行管理,在前后端分离的情况下,一般通过 token 的方式来存储用户信息。maximumSessions 表示最大的用户登录数量。

5、在处理异常的情况时,配置异常信息的处理器,通常是错误页或者返回错误信息,accessDeniedHandler 即权限异常处理器

6、在登录时,还会有记住我的选项,需要配置 token 的配置信息,以及记住我的参数信息。

7、csrf/cors/frameoptions/cache 的配置方式。

8、此外还会有 addFilterAt/addFilterAfter/addFilterBefore 的方式来处理自定义的过滤器。

spring-security 的其它配置如下所示,可以配置信息提示的国际化,身份认证的方式,自定义 AuthenticationManager,以及配置token 的持久化方式,这里包括内存方式和数据库方式。

如果登录需要配置验证码的方式,则需要配置验证码的方式,这里需要处理图片验证码。配置密码处理器,这里为了方便起见,使用的是明文密码。资源的配置可以配置静态资源的方式,来配置静态资源和某些接口的访问权限。

KaptchaFilter 是用来配置验证码的处理,通常情况下登录需要需要 post 的方式,对于验证码的处理方式,需要先生成验证码存储到内存中,然后登录时需要携带验证码参数来进行校验而后进行下一个过滤器处理。

最后,需要注意的是,在设置登录后成功的主页面时,如果配置的是请求转发的方式,其访问方式需要和登录处理器的 http method 保持一致。

4 spring security 表单认证流程

通过上述的表单认证配置,通过源码分析可以得知如下的表单认证流程,具体如下图所示:

1、SecurityContextPersistenceFilter 拦截所有的请求,共享会话将 session 中的 SecurityContext 信息放进线程上下文中。

2、AbstractAuthenticationProcessingFilter 判断是否需要认证,需要认证就获取认证 Authentication,然后放进 session 中。

3、未认证的请求由 UsernamePasswordAuthenticationFilter 创建 UsernamePasswordAuthenticationToken 进行认证,委托 ProviderManager (即 AuthenticationManager) 发起认证,由多个 AuthenticationProvider 接口列表组成进行循环处理。

4、DaoAuthenticationProvider 调用 UserDetailsService 方法进行用户查询,最终返回 AuthenticationToken 即 UsernamePasswordAuthenticationToken

5、整个过程中出现认证失败情况交由 ExceptionTranslationFilter 进行处理

6、所有的配置拦截规则在 FilterSecurityInterceptor 中体现,判断用户是否有权限访问资源。

5 手机号和邮箱验证码登录

通过以上的分析,已经明晰了 spring security 认证相关的内容,通常情况下,系统的登录不仅有账号密码登录,还有手机号验证码、第三方授权等登录多种方式,在接下来将要介绍的就是手机号验证码登录和邮箱登录的方式。

接下来将要重点介绍的如下:

  • 继承 AbstractAuthenticationProcessingFilter,重写 attemptAuthentication 用于获取登录提交参数,并校验验证码是否正确
  • 继承 AuthenticationProvider,重写 authenticate 方法,从数据库查询用户信息,并完成 AuthenticationToken 对象封装。
  • 继承 AbstractAuthenticationToken, 重写构造方法,完成对象封装以及授权。

如下图所示,完成如下配置即可完成手机号验证码登录的功能。

核心的两处代码如下图所示:参数的获取以及请求的token 封装认证。

用户认证成功后封装用户信息。

除此之外,还有发送验证码的接口,通常验证码会存储在 redis 中,通过凭证来从缓存中获取并校验。

手机号登录流程如下:

  1. 调用发送验证码接口,发送验证码,本例是在控制台打印验证码。
  2. 通过表单或者json格式都可以提交信息完成认证。
  3. 用户认证成功或者失败时,通过认证成功或者失败处理器进行处理,返回 json 数据格式或者是页面。

终于搞懂了spring security 相关的认证流程和认证成功的处理方式,此外,还可以通过如下方式,通过注入 AuthenticationManager 调用认证方式来处理用户认证信息。

6 总结

本文主要介绍了 spring security 登录相关的核心配置,以及验证码方式登录的实践, spring security 作为系统开发中常用的权限认证框架,在此基础上拓展了项目的多种登录方式,即手机验证码和邮箱验证码的方式,同时也介绍了前后端分离与不分离情况下的差异。

本文总结了 spring security 常用的使用方式,可以很好的为后续开发提供借鉴。所涉及的代码已经上传至 gitee:https://gitee.com/xieyue86/springsecurity-learn

文章目录
  1. 1. 1 前言
  2. 2. 2 spring security 认证和权限流程
  3. 3. 3 spring security 登录配置
  4. 4. 4 spring security 表单认证流程
  5. 5. 5 手机号和邮箱验证码登录
  6. 6. 6 总结