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 中,通过凭证来从缓存中获取并校验。
手机号登录流程如下:
调用发送验证码接口,发送验证码,本例是在控制台打印验证码。
通过表单或者json格式都可以提交信息完成认证。
用户认证成功或者失败时,通过认证成功或者失败处理器进行处理,返回 json 数据格式或者是页面。
终于搞懂了spring security
相关的认证流程和认证成功的处理方式,此外,还可以通过如下方式,通过注入 AuthenticationManager
调用认证方式来处理用户认证信息。
6 总结
本文主要介绍了 spring security
登录相关的核心配置,以及验证码方式登录的实践, spring security
作为系统开发中常用的权限认证框架,在此基础上拓展了项目的多种登录方式,即手机验证码和邮箱验证码的方式,同时也介绍了前后端分离与不分离情况下的差异。
本文总结了 spring security
常用的使用方式,可以很好的为后续开发提供借鉴。所涉及的代码已经上传至 gitee:https://gitee.com/xieyue86/springsecurity-learn
来源:juejin.cn/post/7329144188926967860