Spring Security 认证执行流程

本文基于 Spring Security 5.x

推荐阅读:

项目集成Spring Security

SpringSecurity 整合 JWT

一、外层-正常登陆调用

项目启动后会自动寻找 UserDetailsService 实现类;

执行 UserDetailsService 的唯一方法 loadUserByName(String username) 并返回 UserDetail 类,注意,返回的 UserDetail 是根据用户名去数据库查询到用户信息;

拿到 UserDetail 后会对 UserDetail 进行一个预检查;

预检查啥?

用户是否存在,是否被锁定等等等;

全部认证成功后会调用 AuthenticationSuccess 成功处理类,失败则调用 AuthenticationFailHandler 类;

此时对于前后端分离项目而言,调用成功处理类,通常是返回由 JWT 等生成的 token json 字符串,前台拿到返回信息后,保存 token 致本地,然后每次请求都会拼接到 head 中。

二、内层-源码级别

以访问某个项目中已有的链接为例:

http://localhost:7777/tmax/videoCategory/getAll

输入用户名、密码后点击登录按钮,首先进入 UsernamePassworkAuthenticationFilter 的父类
AbstractAuthenticationProcessingFilter 调用 doFilter() 方法,然后再执行 UsernamePasswordAuthenticationFilter 的 attemptAuthentication() 方法进行验证;

UsernamePassworkAuthenticationFilter 类,顾名思义,表单登陆过滤器,该类中重点是 attemptAuthentication() 方法:

该方法中通过 用户名+密码= 实例化一个 UsernamePasswordAuthenticationToken 的对象,作用是将用户请求的信息(用户名、密码、seeesion等)封装到该对象中,我们点击进入该对象的构造器如下图所示:

需要说明一点的是,super((Collection)null); collection 代表权限列表,在这传了一个 null 进去是因为刚开始并没有进行认证,因此用户此时没有任何权限,并且设置没有认证的信息 setAuthenticated(false)

再回到 UsernamePassworkAuthenticationFilter attemptAuthentication() 方法,可以看到方法最后调用了 getAuthenticationManager() 方法,然后就进入了 AuthenticationManager 接口的实现类 ProviderManager 中。

补充:AuthenticationManager 不包含验证用户名以及密码的功能,只是用来管理 AuthenticationProvider,所有的校验规则都是写在 AuthenticationProvider 中的;

继续走,在 ProviderManager 这个实现类中,它会调用AuthenticationProvider 接口的实现类获取用户的信息,用户的信息权限的验证就在该类中校验。

进入 ProviderManager 类后会调用 authenticate(Authentication authentication) 方法,它通过 AuthenticationProvider 实现类获取用户的登录的方式,然后会有一个 while 迭代器模式的循环遍历,检查它是否支持这种登录方式,具体的登录方式有表单登录,qq登录,微信登录等。如果最终都不支持会抛出相应的异常信息,如果支持则会进入AuthenticationProvider 接口的抽象实现类 AbstractUserDetailsAuthenticationProvider 中。

进入 AbstractUserDetailsAuthenticationProvider 类后会调用 authenticate(Authentication authentication) 方法对用户的身份进行校验,首先是判断用户是否为空,这个 user 是 UserDetail 的对象,如果为空,表示还没有认证,就需要调用 retrieveUser 方法去获取用户的信息,这个方法是抽象类 AbstractUserDetailsAuthenticationProvider 的扩展类DaoAuthenticationProvider 的一个方法。

在该扩展类的 retrieveUser 方法中调用 UserDetailsService 这个接口的实现类的 loadUserByUsername 方法去获取用户信息,而这里我自己编写了实现类 UserDetailsServiceImpl 类,在这个实现类中,我们可以编写自己的逻辑,从数据库中获取用户密码等权限信息返回。

本地 UserDetailService 实现类 UserDetailsServiceImpl:

在拿到用户的信息后,返回到 AbstractUserDetailsAuthenticationProvider 类中调用 createSuccessAuthentication(principalToReturn, authentication, user) 方法,在该方法中会调用三个参数的UsernamePasswordAuthenticationToken 构造器,不同于前面调用两个参数的,因为这里已经验证了用户的信息和权限,因此不再是给父类构造器中传null 值了,而是用户的权限集合,并且设置认证通过setAuthenticated(true)

如下是 UsernamePasswordAuthenticationToken 构造器:

此时 authorities 不再为空了。

在 UsernamePasswordAuthenticationToken 的父类中,它会检查用的权限,如果有一个为 null,表示权限没有相应的权限,抛出异常。

然后在 createSuccessAuthentication 方法返回后回到 ProvioderManager 的 authenticate 方法中返回 result,最后回到UsernamePasswordAuthenticationFilter 的刚开始进入的 attemptAuthentication 方法中返回。

attemptAuthentication() 方法中的返回,返回到哪?

再回到第一张图,UsernamePasswordAuthenticationFilter 父类 doFilter() 方法,返回值就是 authResult,如果过程中发现存在异常则执行 unsuccessfulAuthentication.onAuthenticationFailure() 方法,如果认证成功则执行 successfulAuthentication.onAuthenticationSuccess() 方法,再结合上边提到的自定义 成功/失败处理类。

最后总结

流程大致是,首先进入 UsernamePasswordAuthenticationFilter 父类 AbstractAuthenticationProcessingFilter 执行 doFilter() 方法,这个 doFilter() 方法呢执行如下:

  1. 判断当前的 filter 是否可以处理当前请求,不可以的话则交给下一个 filter 处理;
  2. 抽象方法由子类 UsernamePasswordAuthenticationFilter 实现;
  3. 认证成功后,调用 sessionStrategy.onAuthentication() 处理一些与 session 相关的方法,最后再调用认证成功处理类,主要将当前的认证放到SecurityContextHolder中;
  4. 认证失败后则调用 认证失败处理类;

习惯在微信看技术文章,想要获取更多的Java资源的同学,可以关注微信公众号:niceyoo

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/414415.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

ScrollView嵌套ViewPager,ViewPage动态设置高度,嵌套事件冲突——滑动冲突解决方法

1、创建自定义ViewPager public class ViewPagerForScrollView extends ViewPager {int myh0 ;boolean ifme false;public ViewPagerForScrollView(Context context) {super(context);Log.i("lgq","高度111 " );}public ViewPagerForScrollView(Context c…

centos7下python3与python2共存并且开启py3虚拟环境

因为下载视频需要用到python3环境,今天在我的win上安装下载工具死活安装不上去,在大盘鸡上一下就安装成功了...可能在win上不兼容吧...无奈只能在大盘鸡上进行折腾了,顺便几个笔记 由于大盘鸡上好多程序基于python2的,所以就没法直…

Android开发启动未注册的activity,Hook使用demo

三个工具类 1、 /*** author : LGQ* date : 2020/05/11 14* desc :*/ public class HCallback implements Handler.Callback{private final String TAG"HCallback";private Handler mHandler;public HCallback(Handler handler){mHandlerhandler;}Overridepublic bo…

游戏UI系统设计

1.需要实现的功能 UI界面的管理(窗体加载、窗体显示、窗体隐藏、窗体销毁等)UI分层次(比如弹窗、广播信息需要在上层)UI界面的出场、入场动画UI界面的显示效果(比如带透明背景、带高斯模糊背景等)UI可以动态…

String转List,String转数组,List转ArrayList,数组转List

String转List private List<String> strings; strings Arrays.asList(dataBean.getImgs().split(",")); String转数组 String ac "12&/*?*/3"; String shuzu [] null; shuzu ac.split("");//是[12&/,?,3] 特殊字符用中括…

docker容器配置加速器

1.编辑docker配置文件 vi /etc/docker/daemon.json加入如下配置&#xff1a; {"registry-mirrors":["https://docker.mirrors.ustc.edu.cn"]}该链接为中科大加速器地址&#xff0c;无需注册。 2. 执行命令生效 systemctl daemon-reload systemctl res…

软件测试原则

软件测试原则测试 原则 测试原则 测试证明软件存在缺陷 测试的本质是证明软件存在缺陷&#xff0c;而不是软件没有缺陷。 人无完人&#xff0c;只要是人写的代码&#xff0c;肯定不能保证百分之百正确&#xff0c;除非特别简单的功能。即便如此&#xff0c;也会存在各种环境问题…

Unable to add window token null is not valid is your activity running

莫名其妙的报错 错误原因是不在主线程调用了弹窗方法 解决方法很简单 创建Handler static final int REFRESH_COMPLETE 0X1112; private Handler mHandler new Handler() {public void handleMessage(android.os.Message msg) {switch (msg.what) {case REFRESH_COMPLETE:Ob…

01-Flutter移动电商实战-项目学习记录

01-Flutter移动电商实战-项目学习记录 一直想系统性的学习一下 Flutter&#xff0c;正好看到该课程《Flutter移动电商实战》的百度云资源&#xff0c;共 69 课时&#xff0c;由于怕自己坚持不下去&#xff08;经常学着学着就不学了&#xff09;&#xff0c;故采用博客监督以记之…

matplotlib 的一些知识

import matplotlib.pyplot as plt plt做图有两种方式&#xff0c;一种是面向对象编程方式的&#xff0c;一种是直接利用plt的结构化的快速绘图编程方式。所以命令不能用错地方。figplt.figure() 返回的fig为Figure类对象 ax1fig.add_subplot(111) 返回的ax1为Axes类对象 这个…

02-Flutter移动电商实战-建立项目和编写入口文件

02-Flutter移动电商实战-建立项目和编写入口文件 环境搭建请参考之前写的一篇文章&#xff1a;Flutter_初体验_创建第一个应用 1、创建项目 采用AndroidStudio构建本项目&#xff0c;FIle>New>New Flutter Project… 创建后的项目如下图所示&#xff1a; 我们着重需要…

Android 倒计时——Timer和CountDownTimer的使用,实现启动,暂停,继续,重复,重设时长以及启动service后台倒计时

实现效果 单个倒计时功能 列表倒计时功能 自定义倒计时类 public class CountDownTimerSupport implements ITimerSupport {private Timer mTimer;private Handler mHandler;/*** 倒计时时…

cin和scanf读入速度

cin慢是有原因的&#xff0c;其实默认的时候&#xff0c;cin与stdin总是保持同步的&#xff0c;也就是说这两种方法可以混用&#xff0c;而不必担心文件指针混乱&#xff0c;同时cout和stdout也一样&#xff0c;两者混用不会输出顺序错乱。正因为这个兼容性的特性&#xff0c;导…

03-Flutter移动电商实战-底部导航栏制作

03-Flutter移动电商实战-底部导航栏制作 1、cupertino_IOS风格介绍 在Flutter里是有两种内置风格的&#xff1a; material风格&#xff1a; Material Design 是由 Google 推出的全新设计语言&#xff0c;这种设计语言是为手机、平板电脑、台式机和其他平台提供一致&#xff…