SpringSecurity认证流程

引入SpringSecurity依赖之后,

<!--springsecurity-->
<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-security</artifactId>
</dependency>

启动项目取访问任意一个URL的时候就会被SpringSecurity自带的过滤器拦截并自动跳转到login登陆界面。只有正确的username和password才会通过校验。SpringSecurity自带的认证方式是基于内存的随即用户,我们可以把它替换成我们自己的认证方式,比如说输入用户名与密码,然后从数据库中认证。
有了这个想法们就要去看看它的过滤器到底是怎么认证的。
要清楚,肯定是过滤器在发挥作用,所以,先从过滤器中查找,看看到底是哪一个过滤器在发挥作用。对项目进行Debug,查看原始过滤器链中的过滤器,发现UsernamePasswordAuthenticFilter最有可能是进行用户名与密码校验的,所以深入这个过滤器去看看。
在这里插入图片描述
UsernamePasswordAuthenticFilter过滤器长这样
在这里插入图片描述
我们都知道,过滤器是需要执行doFilter()方法的,但是我翻遍了这个类,都没有发现此方法,所以,他一定是执行了父类的这个方法,所以,去它的父类看看,也就是AbstractAuthenticationProcessingFilter。进入这个类并查看它的方法。
在这里插入图片描述
可以看到有两个doFilter方法,其中肯定有一个是自己写的,并一个是重写通用过滤器Filter的doFilter方法的。深入进去看看。
在这里插入图片描述
可以看到,在必定被调用的doFilter方法中(也就是被重写的那个)调用了private的doFilter方法,而这个重载的doFilter就应该是这个AbstractAuthenticationProcessingFilter要执行的逻辑。看看这个方法有什么。
在这里插入图片描述
逻辑大概4部分,先判断需不需要认证,如果不需要,那就放行;如果需要认证,那就执行以下代码

Authentication authenticationResult = attemptAuthentication(request, response);

然后拿到认证结果之后,就去做认证成功或者认证失败的逻辑。也就是说,具体的认证工作是在attemptAuthentication中完成的。而UsernamePasswordAuthenticFilter中重写了这个方法,那这个方法就是具体的认证方法。
在这里插入图片描述
大致逻辑有三步,第一,看看请求方式对不对;第二,拿出属性值,做赋值等操作,并没有做具体的方法逻辑;第三,调用代码

return this.getAuthenticationManager().authenticate(authRequest);

在这里插入图片描述
这个方法只是返回一个对象,而且这个对象并没有显示创建,所以,可能是由Spring自动创建的,而后注入进去。拿到这个AuthenticationManager类型的对象之后,就去调用了它的authenticate方法了。具体看看
在这里插入图片描述
点进去之后,发现是一个接口方法,是需要实现的,但是这个接口的实现类很多。
在这里插入图片描述
我也不知道具体是哪一个,那就打断点,看看他具体去了哪里。在

return this.getAuthenticationManager().authenticate(authRequest);

打断点,然后运行项目,访问一个需要认证的URL,进入它自动跳转的login,然后输入username和password,点击登录。
在这里插入图片描述
点这个两下,进入具体的认证方法里。
在这里插入图片描述
看,进入了ProviderManager中的authentication中。接下来看看方法里的具体逻辑。
在这里插入图片描述
逻辑大致三部分,首先判断这个provider支不支持我传进来的对象,支持的话会执行以下代码(中间logger可以跳过,因为是关于日志的)

result = provider.authenticate(authentication);

继续点进去,看看具体方法。
在这里插入图片描述
里边有一个接口,那继续看他的实现类,看看到底是哪一个在发挥作用。
在这里插入图片描述
这个接口的实现类太多了,只能在这里打断点,再次debug。
这次代码运行到了这里,叫AbstractUserDetailsAuthenticationProvider。
在这里插入图片描述
而抽象类很接口类似,也需要实现类,这个抽象类的实现类只有一个。
在这里插入图片描述
也可以在Debug的控制台上看,这个比较直观。
在这里插入图片描述
因为DaoAuthenticationProvider里没有重写authenticate方法,所以执行的是父类中的这个方法。这个代码的以下片段
在这里插入图片描述
调用了retrieveUser方法,因为子类中重写了,所以DaoAuthenticationProvider实例执行自己中的这个方法,这个方法返回一个user对象,而且有异常的话,直接抛出了UsernameNotFoundException。所以,我就怀疑这个retrieveUser方法可能会执行查询用户的逻辑。所以深入这个方法查看。
在这里插入图片描述
在DaoAuthenticationProvider重写的这个方法里,有一句代码很清楚,叫

UserDetails loadedUser = this.getUserDetailsService().loadUserByUsername(username);

就是根据用户名去查找用户的,也就是认证里最核心的代码了。
那么再次看看这个loadUserByUsername是谁去执行的、去执行了什么。点进去一看,还是一个接口。
在这里插入图片描述
需要对UserDetailsService这个接口做出实现。看看它的实现类吧。
在这里插入图片描述
很明显,是InMemoryUserDetailsManager。看看它的loadUserByUsername在干什么。
在这里插入图片描述
通过用户名从this.users里获取用户,
在这里插入图片描述
而users是一个map。那用户从哪里来?
在这里插入图片描述
可以看到,它的用户都是基于内存自己创建的。这样,认证逻辑也就说明白了

而我们要从数据库里获取用户,那就需要吧InMemoryUserDetailsManager换成我们自己的,也就是需要自己实现一个的UserDetailsService接口,实现类就是执行从数据库中取数据的逻辑。

后续请看主页~

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值