过前面对SpringSecurity安全过滤器工作原理的分析( [Spring Security安全过滤器工作原理](滑动验证页面)我们知道Spring Security的用户认证过程的工作原理大概为:
1. SecurityContextPersistenceFilter过滤器负责基于session的用户认证信息的管理。
2. UsernamePasswordAuthenticationFilter负责用户登录认证,认证成功后将认证信息写入SecurityContextHolder中。
3. FilterSecurityInterceptor负责验证当前用户是否符合当前请求的安全策略要求。
所以我们知道用户认证是在UsernamePasswordAuthenticationFilter过滤器完成的,如果我们要实现自定义的用户登录认证的话,只要想办法替换掉UsernamePasswordAuthenticationFilter过滤器中的原装缺省用户认证过程,应该就可以了。
接下来我们研究:
1. 什么是用户认证。
2. UsernamePasswordAuthenticationFilter过滤器的缺省认证过程。
3. 如何替换掉UsernamePasswordAuthenticationFilter的缺省用户认证过程。
#### 什么是用户认证
这个问题其实我们并不研究、只是声明,明确一下我们所要研究的问题的主题。
Spring Security官网overview部分开宗明义说明SpringSecurity是什么的时候就明确提过用户认证:
> Spring Security is a framework that provides authentication, authorization, and protection against common attacks.
说明用户认证是Spring Security的重要主题之一。
我们简化、明确一下今天要研究的用户认证这个主题实际就是:用户身份认证,让系统搞清楚你是否是合法用户的过程。
目前项目上或者我们见过的系统中用的最多的身份认证方式还是用户名、密码(或者短信验证码),用户身份认证其实包含两部分内容:
1. 身份确认:通过登录方式完成。
2. 身份保持及验证:就是登录验证完成之后,用户身份认证信息在保存以及后续的用户访问过程中的验证问题。
第一个问题比较简单明确,第二个问题就会引入两种不同的主流框架,主要区别是用户身份认证信息如何保存:
1. 服务器端保存:最常见的session方式。
2. 客户端持有:比如JWT的方式
我们今天要讨论的是***身份认证***的过程,忽略或简化身份信息的保存和持有过程,或者说我们就以传统的、Spring Security默认提供的通过session方式在服务器端持有用户身份认证信息的方式,来把今天研究的主题聚焦在***身份认证***这个问题上。
#### UsernamePasswordAuthenticationFilter过滤器的身份认证过程
先来认识几个概念:
1. Authentication:或者叫AuthenticationToken,默认实现是UsernamePasswordAuthenticationToken,用来持有请求上来的用户名、密码等信息,以及最终通过认证后的用户认证信息。
2. AuthenticationManager:认证管理器,由他来发起认证,但是他不完成认证,而是有他持有的AuthenticationProvider完成。
3. AuthenticationProvider:如上。
4. UserDetails:系统合法用户信息,比如你的系统的合法用户保存在数据库中,那么在发起认证的过程中就需要从数据库获取到后保存在UserDetails中,对应的有一个叫UserDetailsService的家伙,负责干这个事。
5. PasswordEncoder:顾名思义,密码编码器,系统用户的密码在系统中一定是以密文保存,前台提交上来的一般是明文,需要通过PasswordEncoder验密。说白了你的密码加密算法就是在PasswordEncoder中实现。
差不多够了,我们就用上面几个概念来说明一下UsernamePasswordAuthenticationFilter过滤器的用户登录认证过程。
第一步用request生成UsernamePasswordAuthenticationToken,此时生成的UsernamePasswordAuthenticationToken对象中只有前端登录页面传上来的用户名、密码,该token尚未完成认证。我们假设前台送上来的用户叫:user。前台录入的密码是:123456。
第二步,获取到AuthenticationManager进行认证,上面说过了,他是manager,他不干活,交给AuthenticationProvider去干。
第三步,AuthenticationProvider开始干活,他首先获取到UserDetailsService,让他获取到系统中叫user的用户信息,返回UserDetails对象(假设获取到了,对象名叫sysUser)。
第四步,依然是由AuthenticationProvider继续干,拿着前端送上来的用户名、密码(当前由UsernamePasswordAuthenticationToken对象持有)、以及后台获取到的用户对象(sysUser),交给PasswordEncoder去验密。
第五步,AuthenticationProvider继续拼命干活,如果认证通过的话(密码一样),重新生成一个UsernamePasswordAuthenticationToken,这是已经完成认证的token。
第六步,AuthenticationProvider干完活了交给经理AuthenticationManager,经理甩手把结果交给过滤器UsernamePasswordAuthenticationFilter。
第七步,过滤器把最终认证过的UsernamePasswordAuthenticationToken保存在SecurityContextHolder中,供后续过滤器、尤其是FilterSecurityInterceptor验证使用。
UsernamePasswordAuthenticationFilter完成使命!
#### 如何替换掉UsernamePasswordAuthenticationFilter的缺省用户认证过程
这个标题长的我有点不太适应,但是就这样吧。
这个问题你如果去百度或者google的话,会有很多不同的答案,我理解由于Spring Security本身是Spring家族的一员,与Spring或SpringBoot结合之后留给用户的客户化方式众多,所以我们可以任选其一进行客户化。
任何既能够实现你的目标、又没有给你的项目带来负面影响方案,都是可行的。
下面我们开始实现这一目标。
通过上面对UsernamePasswordAuthenticationFilter工作过程的分析,我们可以先设想一个思路:***替换掉上述第三步中的UserDetailsService,让我们自己的UserDetailsService干活,去获取我们自己的用户***。
我认为这是比较简单的方案,你当然可以替换掉整个UsernamePasswordAuthenticationFilter,但是这样的话你就相当于是替换掉了Spring Security的一个重要模块,你就可以怀疑他还是不是Spring Security了。
篇幅原因,本篇分析原理,下一篇文章具体操作。