AuthenticationManager 的 authentication 过程
1. 结论
// 调用链
AuthenticationManager.authenticate() --> ProviderManager.authenticate() --> DaoAuthenticationProvider(AbstractUserDetailsAuthenticationProvider).authenticate()
// 处理
在最后的 authenticate() 方法中,调用了 UserDetailsService.loadUserByUsername() 并进行了密码校验,校验成功就构造一个认证过的 UsernamePasswordAuthenticationToken 对象放入 SecurityContext.
2. AuthenticationManager.authenticate()
public interface AuthenticationManager {// 就这一个方法Authentication authenticate(Authentication authentication)throws AuthenticationException;
}
ProviderManager
是它的是实现类,实际上调用的它的 authentication()
方法
3. ProviderManager.authentication()
public class ProviderManager implements AuthenticationManager, MessageSourceAware,InitializingBean {// authenticate 方法public Authentication authenticate(Authentication authentication)throws AuthenticationException {// getProviders() -> debug发现其实就一个 DaoAuthenticationProvider.classfor (AuthenticationProvider provider : getProviders()) {// toTest == UsernamePasswordAuthenticationToken.class// 其实就是判断 toTest 是不是UsernamePasswordAuthenticationToken类,成立if (!provider.supports(toTest)) {continue;}try {=========================================================================================// 最终 DaoAuthenticationProvider.authenticate()result = provider.authenticate(authentication);
=========================================================================================
}
4. DaoAuthenticationProvider.authenticate()
DaoAuthenticationProvider
的父类是 AbstractUserDetailsAuthenticationProvider
,实际上调用的是父类的 authentication()
,因为继承关系,所以有时候不好判断到底是父类的方法还是子类重写后的方法,建议 debug,非常清楚。
这里主要有三个步骤:
- 子类的
retrieveUser()
,里面调用了UserDetailsService.loadUserByUsername()
进行身份查找 - 子类的
additionalAuthenticationChecks()
,里面调用passwordEncoder.matches()
进行密码匹配 - 子类的
createSuccessAuthentication()
,认证完成,往SecurityContext
中放一个认证过的 auth 对象
public abstract class AbstractUserDetailsAuthenticationProvider implementsAuthenticationProvider, InitializingBean, MessageSourceAware {// authenticationpublic Authentication authenticate(Authentication authentication)throws AuthenticationException {// ....
========================================================================================try {// 1. 这个 retrieveUser ,就是判断用户身份,查询内存或者数据库拿到用户对象user = retrieveUser(username,(UsernamePasswordAuthenticationToken) authentication);}// 2. 密码校验additionalAuthenticationChecks(user,(UsernamePasswordAuthenticationToken) authentication);Object principalToReturn = user;// 3. 返回一个认证过的对象,注意这里 principle 传的是保存了用户信息的 user 对象,不是 Stringreturn createSuccessAuthentication(principalToReturn, authentication, user);}
5. DaoAuthenticationProvider
public class DaoAuthenticationProvider extends AbstractUserDetailsAuthenticationProvider {// 1. retrieveUser(),用户身份判断protected final UserDetails retrieveUser(String username,UsernamePasswordAuthenticationToken authentication) {try {=========================================================================================// 就是在这里调用了 UserDetailsService UserDetails loadedUser = this.getUserDetailsService().loadUserByUsername(username);
=========================================================================================}
// 2. additionalAuthenticationChecks() 密码校验
protected void additionalAuthenticationChecks(UserDetails userDetails,UsernamePasswordAuthenticationToken authentication)throws AuthenticationException {if (authentication.getCredentials() == null) {logger.debug("Authentication failed: no credentials provided");throw new BadCredentialsException(messages.getMessage("AbstractUserDetailsAuthenticationProvider.badCredentials","Bad credentials"));}String presentedPassword = authentication.getCredentials().toString();if (!passwordEncoder.matches(presentedPassword, userDetails.getPassword())) {logger.debug("Authentication failed: password does not match stored value");throw new BadCredentialsException(messages.getMessage("AbstractUserDetailsAuthenticationProvider.badCredentials","Bad credentials"));}}// 3. 创建一个已认证的对象protected Authentication createSuccessAuthentication(Object principal,Authentication authentication, UserDetails user) {boolean upgradeEncoding = this.userDetailsPasswordService != null&& this.passwordEncoder.upgradeEncoding(user.getPassword());// 这里又回到 父类了,无语return super.createSuccessAuthentication(principal, authentication, user);}// 直接粘贴在这里 super.createSuccessAuthentication()protected Authentication createSuccessAuthentication(Object principal,Authentication authentication, UserDetails user) {// 调用 UsernamePasswordAuthenticationToken 类 三个构造参数的构造器(认证过的对象创建)UsernamePasswordAuthenticationToken result = new UsernamePasswordAuthenticationToken(principal, authentication.getCredentials(),authoritiesMapper.mapAuthorities(user.getAuthorities()));result.setDetails(authentication.getDetails());return result;}
6. UsernamePasswordUnthenticationToken构造器
为什么说选择三个参数的构造器构造的就是一个已认证的对象。我们来看看
/*** 两个参数,构造待认证对象* principle 传 username* credentials 传 passord*/
public UsernamePasswordAuthenticationToken(Object principal, Object credentials) {super(null);this.principal = principal;this.credentials = credentials;// 设置标识,未认证setAuthenticated(false);
}/*** This constructor should only be used by <code>AuthenticationManager</code> or* <code>AuthenticationProvider</code> implementations that are satisfied with* producing a trusted (i.e. {@link #isAuthenticated()} = <code>true</code>)* authentication token.** 两个参数,构造待认证对象* principle 传 保存了用户信息的对象(实现了UserDetails接口的对象)* credentials 传 passord,也可以传null,因为认证过后,我们不需要知道密码,也是一种保护* authorities 传 用户的权限列表*/
public UsernamePasswordAuthenticationToken(Object principal, Object credentials,Collection<? extends GrantedAuthority> authorities) {super(authorities);this.principal = principal;this.credentials = credentials;// 标记已认证 super.setAuthenticated(true);
}
从这里也可以看出为什么 pinciple
和 credentials
的类型都是 Object
,因为 pinciple
认证前需要传 String
类型的 username
,认证后需要传实现了UserDetails
接口的用户对象;而 credentials
认证前需要传前端传来的String
类型的password
,认证后需要传 String
类型的密码 或者 null
。
通常都是,接收 前端传来的 username
和 password
,调用 两个参数的构造器,创建一个未认证的对象,交给AuthenticationManager
进行认证。认证成功后,调用三个参数的构造器,创建一个已认证的对象。
AuthenticationManager 的 authentication 过程相关推荐
- 基于SAML2.0的SAP云产品Identity Authentication过程介绍
SAP官网的架构图 https://cloudplatform.sap.com/scenarios/usecases/authentication.html 上图介绍了用户访问SAP云平台时经历的Au ...
- AuthenticationManager验证原理分析
本文来说下AuthenticationManager验证原理 文章目录 AuthenticationManager概述 AuthenticationManager相关类图 security认证流程 A ...
- ASP.NET Core Identity 实战(4)授权过程
这篇文章我们将一起来学习 Asp.Net Core 中的(注:这样描述不准确,稍后你会明白)授权过程 前情提要 在之前的文章里,我们有提到认证和授权是两个分开的过程,而且认证过程不属于Identity ...
- Spring Security认证过程
2019独角兽企业重金招聘Python工程师标准>>> Spring Security是一个能够为基于Spring的企业应用系统提供声明式的安全访问控制解决方案的安全框架.它提供了一 ...
- 一张大图了解ASP.NET Core 3.1 中的Authentication与Authorization
下面是一张ASP.NET Core 3.1 中关于Authentication与Authorization的主流程框线图,点击这里查看全图:https://johnnyqian.net/images/ ...
- Authentication和Authrization(上)
两个案例 第一个案例是几个月前,我收到一位朋友的邮件,邀请我加入一个叫ShoppyBag的网站.我去看了,没想到注册后就让我登录GMail帐号,并且GMail提示我该网站需要访问我的通讯录,于是我当即 ...
- Spring Security:身份验证令牌Authentication介绍与Debug分析
在Spring Security中,通过Authentication来封装用户的验证请求信息,Authentication可以是需要验证和已验证的用户请求信息封装.接下来,博主介绍Authentica ...
- print在python2和python3的区别_Python2和Python3中print的不同点
在Python2和Python3中都提供print()方法来打印信息,但两个版本间的print稍微有差异 主要体现在以下几个方面: 1.python3中print是一个内置函数,有多个参数,而pyth ...
- Spring security (一)架构框架-Component、Service、Filter分析
想要深入spring security的authentication (身份验证)和access-control(访问权限控制)工作流程,必须清楚spring security的主要技术点包括关键 ...
最新文章
- randaugment
- 无法访问gcr.io的几种解决办法
- linux 高级命令
- 【Python CheckiO 题解】Multiply (Intro)
- adb push命令传文件到手机_Android调试桥(adb)
- 嵌入式工作笔记0005---嵌入式发展和组成
- NetApp公司的4KB块写入技术真能容纳更多数据吗?
- 如何在C#中生成与PHP一样的MD5 Hash Code
- 盈建科中地震波_SIMQKE_GR、SeismoSignal、中国建筑抗震设计规范反应谱v1.0----时程分析地震波生成及分析必备软件...
- STK X教程—C++/CLI
- YDOOK:ANSYS 谐波分析的要点和主要应用场景 谐波效应的来源
- ACM题库(计蒜客A1001整除问题)
- win2008服务器系统玩红警,win10系统玩红警卡死的两种方法
- 【Java】算法之矩阵的加减乘除运算
- 类加载——类加载时机、类加载过程、类加载器
- 北大AI公开课第十一课--语言智能的进展by微软亚洲研究院周明
- 再议IIC协议与设计【3】 --SCCB总线介绍
- 犀牛插件-获取曲面表面点-Python-坐标点数组-rhino插件
- 源码分享意义何在?为何很多官方软件开发商都会有破解版和bug版本的系统盛行?
- 电脑关机状态重置BIOS
热门文章
- 【Stacking改进】基于随机采样与精度加权的Stacking算法
- 见或不见! see me or not!
- matlab 二分法求最优解
- ThreadPoolExecutor 线程池和redisson加上手动事务踩的坑
- 隔壁老王月月入5W,小王回回考100,这轮回的人生...
- 百度云加速下载Proxyee-down的下载与安装教程2.x
- 如何用Python复现吉布斯现象?
- 抖音uv价值怎么计算?直播间如何提高uv价值?
- mysql的while循环语句,MySQL循环语句之while循环测试
- 用CSS和JS画出来的美女(酷)