SpringSecurity(二)——自定义数据源
前言
本篇笔记中记录SpringSecurity的认证数据源的自定义,即不在配置文件中修改用户认证的用户名和密码(这种认证是将数据源放入到内存中,运行过程中使用使用内存中的数据源来执行认证),我们将会使用自定义数据源(数据库中的数据)来对用户访问系统资源进行认证。
文章目录
概述(文字说明,较为晦涩,看不懂的可以直接看下面的代码实现或者找视频,总结部分会有手绘图)
第一篇笔记中并没有说明SpringSecurity的认证流程,在这里给大家说明一下:
首先我们SpringSecurity的认证是由一个叫做AuthenticationManager
的接口来完成的,这个接口下面默认有且仅有一个实现类——ProviderManager
,认证的具体工作都是由这个实现类来完成。
ProviderManager
对象在认证的时候有一个小细节,那就是他会有通过AuthenticationManager
接口生成一个具体的实例类来做自己的父类,然而该接口只有一个实现类,即它本身,所以他生成的自己的父类也和自己是同一个类型(典型的我是我爸爸了)。
那么在认证流程中,我们的ProviderManager
会先进行认证,如果发现认证失败则会通过自己的父类,即自己生成的同一个类型的实现类按照最基础的配置(默认配置)来进行认证,如果再次认证失败则表示整体认证失败。
在这个过程中,ProviderManager
生成的父类其实是一个默认的全局认证器,该认证器是由SpringBoot集成SpringSecurity框架的时候自动注入且按照默认配置设置的一个全局
对象(记住这个全局,后面要考),我们首先经过ProviderManager
本身而没有限制性父类,代表着我们可以自定义认证规则(具体怎么自定义一会说)。这个时候我们就要引出第三个类——AuthenticationProvider
AuthenticationProvider
这个类是一个认证规则,它存在于ProviderManager
中,这种情况下叫做自定义认证列表,后期在针对不同类型资源自定义多种不同的认证规则的时候,我们回将所有除默认的认证器以外的所有认证器中进行认证比对,只要有一个通过就表示为认证成功;如果所有自定义的认证规则都没有通过,则会进行默认的认证器规则进行比对,如果默认的认证器规则中比对通过后也表示认证通过。
自定义数据源配置
自定义数据源配置中一般来说可以有两种方式,这两种方式如下:
修改默认认证器对象
在上一篇笔记中我们自定义的配置类中可以自己定义一个方法来修改SpringBoot集成SpringSecurity框架时自动注入的默认认证对象。
/**
* 该方法为使用SpringBoot默认注入的SpringSecurity提供的AuthenticationManagerBuilder对象来操作
* 使用这种方式时一定要注意,如果外部重写UserDetailsService 对象的话,一定要将外部重写的该对象注入到这个默认的对象中
*
* @throws Exception
*/
@Autowired
public void initialize(AuthenticationManagerBuilder builder) throws Exception {
log.info("springboot 默认配置:" + builder);
// 规定用户认证信息存于内存中
InMemoryUserDetailsManager userDetailsService = new InMemoryUserDetailsManager();
// 指定用户信息数据
userDetailsService.createUser(User.withUsername("Reman").password("{noop}123").roles("admin","suser").build());
// 将自定义的用户数据加入到默认认证器中
builder.userDetailsService(userDetailsService);
}
在这种自定义的方法中,我们的方法名可以任意命名,只要在内部执行修改默认认证对象的操作即可,同时我们也可以使用最简单的方式来修改。
我们主要需要修改的是用户认证的信息,即:用户名、密码以及权限信息。而这些信息由AuthenticationManagerBuilder
实例中的UserDetailsService
实例来修改用户的三个信息,所以我们可以通过自定义UserDetailsService
配置类来完成修改用户数据,具体如下:
@Bean
public UserDetailsService userDetailsService(){
InMemoryUserDetailsManager userDetailsService = new InMemoryUserDetailsManager();
userDetailsService.createUser(User.withUsername("Reman").password("{noop}123").roles("admin","suser").build());
return userDetailsService;
}
自定义认证器
我们也可以自定义一个认证器对象,然后将之前第定义的用户数据对象注入之中:
/**
* 推荐使用该方法来进行全局认证管理器的配置
* 该方法是自定义AuthenticationManagerBuilder对象管理,自定义的对象管理会覆盖SpringBoot默认注入的SpringSecurity对象
* 这种方式方便我们在项目开发中完全自定义认证管理器
*
* @throws Exception
*/
@Override
protected void configure(AuthenticationManagerBuilder builder) throws Exception {
// 自定义认证管理器时,需要将外部的数据源对象手动注入到对应的自定义的管理其中
builder.userDetailsService(userDetailsService());
log.info("自定义AuthenticationManager:" + builder);
}
自定义认证器对象创建的时候属于本地对象,即在工厂中可以使用,但是并不能对外注入。所以我们需要重写一个方法覆盖一下:
// 用来将自定义的AuthenticationManager对象在工厂中进行暴露,使得该对象在项目中的任何地方都可以注入
@Override
// 在工厂中实例一个对象
@Bean
public AuthenticationManager authenticationManagerBean() throws Exception {
return super.authenticationManagerBean();
}
总结
本篇笔记中记录了自定义认证数据源的一些基本流程和概念。在此重复一下,推荐使用congure
方法直接自定义认证器对象覆盖默认注入的对象。
SpringSecurity中的认证流程一定要理解透彻,否则自定义数据源以及后续的一些自定义拦截器等操作会搞混乱,下面手绘图表现出认证器的流程及结构:
上图阐述的是简单的认证的流程,在我们需要认证的时候,首先会在AuthenticationManager
接口中实现一个类——ProviderManager
,该类在实现后会自行生成一个父类,然而父类是接口,而在默认结构中该接口只有ProviderManager
一个实现类,所以会生成一个相同类型的父类。
图中蓝色虚线圈住的部分可以有多个,我们可以根据资源自定义多种规则的认证器。同时一个ProviderManager
中可以有多个AuthencationProvider
(图中关于最下面的AuthenticationProvider有瑕疵,它应该是一个列表)。
具体认证流程:
- 进入自定义认证器(如果没有则进入默认注入的管理器对象),判断是否符合认证规则;
- 继续进入其他自定义认证器进行判断;
- 符合其中一个认证器的规则,则表示认证通过
- 如果所有自定义认证器都不符合认证规则,则进入父类认证器;
- 父类认证器认证通过则表示认证通过,否则认证失败。