SpringSecurity原理解析(二):认证流程
1、SpringSecurity认证流程包含哪几个子流程?
1)账号验证
2)密码验证
3)记住我—>Cookie记录
4)登录成功—>页面跳转
2、UsernamePasswordAuthenticationFilter
在SpringSecurity中处理认证逻辑是在UsernamePasswordAuthenticationFilter这个过滤
器中实现的,UsernamePasswordAuthenticationFilter 继承于
AbstractAuthenticationProcessingFilter 这个父类。
当请求进来时,在doFilter 方法中会对请求进行拦截,判断请求是否需要认证,若不需要
认证,则放行;否则执行认证逻辑;
1

注意:UsernamePasswordAuthenticationFilter 类中是没有 doFilter 方法的,doFilter
方法是继承自父类 UsernamePasswordAuthenticationFilter 的。
doFilter 方法代码如下:
//执行过滤的方法,所有请求都走这个方法
public void doFilter(ServletRequest req, ServletResponse res, FilterChain chain) throws IOException, ServletException {//请求和应答类型转换HttpServletRequest request = (HttpServletRequest)req;HttpServletResponse response = (HttpServletResponse)res;//判断请求是否需要认证处理,若不需要认证处理,则直接放行if (!this.requiresAuthentication(request, response)) {//放行,往下走chain.doFilter(request, response);} else {//执行到这里,进行认证处理if (this.logger.isDebugEnabled()) {this.logger.debug("Request is to process authentication");}Authentication authResult;try {//处理认证,然后返回 Authentication 认证对象//重点authResult = this.attemptAuthentication(request, response);//认证失败if (authResult == null) {return;}//认证成功之后注册sessionthis.sessionStrategy.onAuthentication(authResult, request, response);} catch (InternalAuthenticationServiceException var8) {this.logger.error("An internal error occurred while trying to authenticate the user.", var8);this.unsuccessfulAuthentication(request, response, var8);return;} catch (AuthenticationException var9) {this.unsuccessfulAuthentication(request, response, var9);return;}//if (this.continueChainBeforeSuccessfulAuthentication) {chain.doFilter(request, response);}//认证成功后的处理this.successfulAuthentication(request, response, chain, authResult);}}protected void successfulAuthentication(HttpServletRequest request, HttpServletResponse response, FilterChain chain, Authentication authResult) throws IOException, ServletException {if (this.logger.isDebugEnabled()) {this.logger.debug("Authentication success. Updating SecurityContextHolder to contain: " + authResult);}//将认证成功后的对象保存到 SecurityContext中SecurityContextHolder.getContext().setAuthentication(authResult);//处理 remember-me属性this.rememberMeServices.loginSuccess(request, response, authResult);if (this.eventPublisher != null) {this.eventPublisher.publishEvent(new InteractiveAuthenticationSuccessEvent(authResult, this.getClass()));}//认证成功后,页面跳转this.successHandler.onAuthenticationSuccess(request, response, authResult);}
上边的核心代码是下边这一行:

attemptAuthentication方法的作用是获取Authentication对象其实就是对应的认证过程,
attemptAuthentication 方法在子类UsernamePasswordAuthenticationFilter 中实现的。
attemptAuthentication 方法代码如下:
//认证逻辑
public Authentication attemptAuthentication(HttpServletRequest request, HttpServletResponse response) throws AuthenticationException {//如果我们设置了该认证请求只能以post方式提交,且当前请求不是post请求,表示当前请求不符合//认证要求,直接抛出异常,认证失败if (this.postOnly && !request.getMethod().equals("POST")) {throw new AuthenticationServiceException("Authentication method not supported: " + request.getMethod());} else {//执行到这里表示开始执行认证逻辑//从请求中获取用户名和密码String username = this.obtainUsername(request);String password = this.obtainPassword(request);if (username == null) {username = "";}if (password == null) {password = "";}username = username.trim();//将用户名和密码包装成 UsernamePasswordAuthenticationToken 对象UsernamePasswordAuthenticationToken authRequest = new UsernamePasswordAuthenticationToken(username, password);//设置用户提交的信息到 UsernamePasswordAuthenticationToken 中this.setDetails(request, authRequest);//getAuthenticationManager():获取认证管理器//authenticate:真正处理认证的方法return this.getAuthenticationManager().authenticate(authRequest);}}
1、
3、AuthenticationManager
AuthenticationManager接口中就定义了一个方法authenticate方法,用于处理认证的请求;
AuthenticationManager 接口定义如下:
public interface AuthenticationManager {//处理认证请求Authentication authenticate(Authentication authentication) throws AuthenticationException;}
在这里AuthenticationManager的默认实现是ProviderManager.而在ProviderManager的
authenticate方法中实现的操作是循环遍历成员变量List<AuthenticationProvider> providers
。该providers中如果有一个AuthenticationProvider的supports函数返回true,那么就会调
用该AuthenticationProvider的authenticate函数认证,如果认证成功则整个认证过程结束。
如果不成功,则继续使用下一个合适的AuthenticationProvider进行认证,只要有一个认证
成功则为认证成功。
authenticate 方法定义如下:
//执行认证逻辑
public Authentication authenticate(Authentication authentication)throws AuthenticationException {//获取 Authentication 对象的类型Class<? extends Authentication> toTest = authentication.getClass();AuthenticationException lastException = null;AuthenticationException parentException = null;Authentication result = null;Authentication parentResult = null;boolean debug = logger.isDebugEnabled();//getProviders():获取系统支持的各种认证方式,如:QQ、微信、微博等等for (AuthenticationProvider provider : getProviders()) {//判断当前的 provider认证处理器 是否支持当前请求的认证类型,若不支持,则跳过if (!provider.supports(toTest)) {continue;}if (debug) {logger.debug("Authentication attempt using "+ provider.getClass().getName());}//执行到这里说明当前认证处理器支持当前请求的认证,try {//执行认证操作result = provider.authenticate(authentication);if (result != null) {copyDetails(authentication, result);break;}}catch (AccountStatusException e) {//。。。。。省略 。。。。。}catch (InternalAuthenticationServiceException e) {//。。。。。省略 。。。。。}catch (AuthenticationException e) {//。。。。。省略 。。。。。}}//如果循环结束后还没找到支持当前请求的认证处理器provider ,且父类不为空,则//尝试调用父类的认证方法进行认证处理if (result == null && parent != null) {// Allow the parent to try.try {result = parentResult = parent.authenticate(authentication);}catch (ProviderNotFoundException e) {//。。。。。省略 。。。。。}catch (AuthenticationException e) {//。。。。。省略 。。。。。}}//清空密码凭证if (result != null) {if (eraseCredentialsAfterAuthentication&& (result instanceof CredentialsContainer)) {// Authentication is complete. Remove credentials and other secret data// from authentication((CredentialsContainer) result).eraseCredentials();}//if (parentResult == null) {eventPublisher.publishAuthenticationSuccess(result);}return result;}// //异常处理if (lastException == null) {lastException = new ProviderNotFoundException(messages.getMessage("ProviderManager.providerNotFound",new Object[] { toTest.getName() },"No AuthenticationProvider found for {0}"));}//if (parentException == null) {prepareException(lastException, authentication);}throw lastException;}
在上边的代码中,我们重点看的是下边这一行:
result = provider.authenticate(authentication);
因为是用户认证,所以这里authenticate方法走是AbstractUserDetailsAuthenticationProvider
类中的实现,
AbstractUserDetailsAuthenticationProvider.authenticate 方法定义如下所示:
//认证操作
public Authentication authenticate(Authentication authentication)throws AuthenticationException {Assert.isInstanceOf(UsernamePasswordAuthenticationToken.class, authentication,() -> messages.getMessage("AbstractUserDetailsAuthenticationProvider.onlySupports","Only UsernamePasswordAuthenticationToken is supported"));// 获取提交的账号String username = (authentication.getPrincipal() == null) ? "NONE_PROVIDED": authentication.getName();//标记,是否使用缓存,默认是使用的,先从缓存中查找提交的账号//若账号已经登录,则缓存中应该boolean cacheWasUsed = true;//根据账号名称从缓存中查找账号,若缓存中不存在该账号,则需要认证UserDetails user = this.userCache.getUserFromCache(username);if (user == null) {//若缓存中不存在该账号,没有缓存cacheWasUsed = false;try {//账号认证user = retrieveUser(username,(UsernamePasswordAuthenticationToken) authentication);}catch (UsernameNotFoundException notFound) {//。。。。。省略 。。。。。}Assert.notNull(user,"retrieveUser returned null - a violation of the interface contract");}try {//如果账号存在,即账号认证成功,则这里就开始密码认证//密码校验前的前置检查,检查账号是否过期、是否锁定等preAuthenticationChecks.check(user);//密码校验//user: 数据库中的数据//authentication: 表单提交的数据additionalAuthenticationChecks(user,(UsernamePasswordAuthenticationToken) authentication);}catch (AuthenticationException exception) {//。。。。。省略 。。。。。}//检查凭证是否过期postAuthenticationChecks.check(user);//将用户保存到缓存中if (!cacheWasUsed) {this.userCache.putUserInCache(user);}Object principalToReturn = user;if (forcePrincipalAsString) {principalToReturn = user.getUsername();}return createSuccessAuthentication(principalToReturn, authentication, user);
}//创建具体的 Authentication 对象
protected Authentication createSuccessAuthentication(Object principal,Authentication authentication, UserDetails user) {// user.getAuthorities():返回用户的权限UsernamePasswordAuthenticationToken result = new UsernamePasswordAuthenticationToken(principal, authentication.getCredentials(),authoritiesMapper.mapAuthorities(user.getAuthorities()));result.setDetails(authentication.getDetails());return result;}
密码前置校验如下图所示:

然后进入到retrieveUser方法中,retrieveUser和additionalAuthenticationChecks 方法
具体的实现是DaoAuthenticationProvider 类中实现的,如下所示
@Overrideprotected final UserDetails retrieveUser(String username, UsernamePasswordAuthenticationToken authentication)throws AuthenticationException {prepareTimingAttackProtection();try {// getUserDetailsService会获取到我们自定义的UserServiceImpl对象,也就是会走我们自定义的认证方法了UserDetails loadedUser = this.getUserDetailsService().loadUserByUsername(username);if (loadedUser == null) {throw new InternalAuthenticationServiceException("UserDetailsService returned null, which is an interface contract violation");}return loadedUser;}catch (UsernameNotFoundException ex) {//。。。。。省略 。。。。。}catch (InternalAuthenticationServiceException ex) {//。。。。。省略 。。。。。}catch (Exception ex) {//。。。。。省略 。。。。。}}//具体的密码校验逻辑
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"));}}
相关文章:
SpringSecurity原理解析(二):认证流程
1、SpringSecurity认证流程包含哪几个子流程? 1)账号验证 2)密码验证 3)记住我—>Cookie记录 4)登录成功—>页面跳转 2、UsernamePasswordAuthenticationFilter 在SpringSecurity中处理认证逻辑是在UsernamePas…...
数据中台 | 数据资源管理平台介绍
01 产品概述 数据资源的盘查、集成、存储、组织、共享等全方位管理能力,无论对于企业的数字化转型,还是对企业数据资产的开发、运营、交易及入表,都具有极为关键的作用。今天,小兵就来为大家介绍我们自研数据智能平台中的核心产品…...
智慧环保平台建设方案
智慧环保平台建设方案摘要 政策导向与建设背景 背景:全国生态环境保护大会提出坚决打好污染防治攻坚战,推动生态文明建设,目标是在2035年实现生态环境质量根本好转。构建生态文明体系,包括生态文化、生态经济、目标责任、生态文明…...
SpringMVC映射请求;SpringMVC返回值类型;SpringMVC参数绑定;
一,SpringMVC映射请求 SpringMVC 使用 RequestMapping 注解为控制器指定可以处理哪些URL请求 1.1RequestMapping修饰类 注解RequestMapping修饰类,提供初步的请求映射信息,相对于WEB应用的跟目录。 注: 如果在类名前࿰…...
【第28章】Spring Cloud之Sentinel注解支持
文章目录 前言一、注解埋点支持二、SentinelResource 注解三、实战1. 准备2. 纯资源定义3. 添加资源配置 四、熔断(fallback)1. 业务代码1.1 Controller1.2 Service1.3 ServiceImpl 2. 熔断配置3. 熔断测试 总结 前言 上一章我们已经完成了对Sentinel的适配工作,这…...
鼎捷新一代PLM 荣膺维科杯 “2023年度行业优秀产品奖”
近日,由中国高科技行业门户OFweek维科网主办的“全数会2024(第五届)中国智能制造数字化转型大会暨维科杯工业自动化及数字化行业年度评选颁奖典礼”在深圳隆重举办。这不仅是中国工业自动化及数字化行业的一大品牌盛会,亦是高科技…...
如何升级用 Helm 安装的极狐GitLab Runner?
本分分享如何对 Helm 安装的 Runner 进行升级。整个过程分为三步:1、确定 Runner 最新版本或者想要升级的版本是否存在;2、用 Helm upgrade 命令进行升级;3、升级确认。 极狐GitLab 为 GitLab 的中国发行版,中文版本对中国用户更…...
08 vue3之认识bem架构及less sass 和scoped
bem架构 他是一种css架构 oocss 实现的一种 (面向对象css) ,BEM实际上是block、element、modifier的缩写,分别为块层、元素层、修饰符层,element UI 也使用的是这种架构 1. BEM架构 1. 介绍 1. BEM是Block Element M…...
静态库的制作
静态库是一组对象文件的集合,它们在编译时被链接到可执行文件中。这意味着,静态库中的代码会被复制到每个使用它的程序中,因此静态库不需要在程序运行时被单独加载。制作静态库可以帮助你将常用的代码模块化、重用,简化开发过程。…...
PHP在现代Web开发中的高效应用与最佳实践
PHP在现代Web开发中的高效应用与最佳实践 在快速迭代的Web开发领域,PHP作为一门历史悠久且广泛应用的服务器端脚本语言,始终保持着其独特的魅力和强大的生命力。从简单的动态网页到复杂的企业级应用,PHP凭借其易学性、丰富的库支持和广泛的社…...
大数据-134 - ClickHouse 集群三节点 安装配置启动
点一下关注吧!!!非常感谢!!持续更新!!! 目前已经更新到了: Hadoop(已更完)HDFS(已更完)MapReduce(已更完&am…...
2024网络安全人才实战能力白皮书安全测试评估篇
9月10日,国内首个聚焦“安全测试评估”的白皮书——《网络安全人才实战能力白皮书-安全测试评估篇》(以下简称“白皮书”)在国家网络安全宣传周正式发布。 作为《网络安全人才实战能力白皮书》的第三篇章,本次白皮书聚焦“安全测…...
[项目][WebServer][解析错误处理]详细讲解
可为每种情况都确实对应一个状态码,当发生错误时,跳转到对应的html页面即可但是为了代码的复用性,可以将所有的错误情况都归置处理 #define SEP ": " #define LINE_END "\r\n" #define WEB_ROOT "wwwroot" #…...
51单片机应用开发---数码管的控制应用
实现目标 1、掌握数码管结构、驱动原理; 2、 一、什么是数码管? 1.数码管定义 数码管,也称为LED数码管,基本单元是发光二极管(LED)。分为七段数码管和八段数码管(多一个小数点DP)。数码管在我们生活中无处不在,比如…...
Vue3+Django5+REST Framework开发电脑管理系统
前端:Vue3TypeScript 后端:Django5REST Framework 功能介绍 用户管理角色管理菜单管理配件管理仓库管理类型管理电脑管理入库管理出库管理库存管理收发明细管理 界面预览 源码地址:managesystem: 电脑管理系统...
Java8函数式接口全攻略
一、接口大白话 1.四大基础接口 Consumer<T> 核心方法:void accept(T t);消费者。接受一个输入参数,不返回任何结果的操作。望文生义:你给我啥,我就执行啥,没有结果。 Supplier<T> 核心方法: T get();供…...
英文软件汉化中文软件教程asi exe dll 等汉化教程
相信大家在使用国际软件的时候,会经常碰到英文类型的软件 或者玩一些游戏使用一些工具,也基本都是外网的,那么对于用户来讲 就会非常的不方便! 小编为大家整理了一些国内大佬出的的英文软件汉化中文软件的视频教程 教程分为EX…...
HTTP 请求方式`application/x-www-form-urlencoded` 与 `application/json` 怎么用?有什么区别?
HTTP 请求方式总结:application/x-www-form-urlencoded 与 application/json 在前后端交互中,客户端发送数据到服务器的常见方式有两种:application/x-www-form-urlencoded 和 application/json。本文将详细介绍这两种请求方式的特点、使用方…...
prometheus 集成 grafana 保姆级别安装部署
前言 本文 grafana 展示效果只需要 prometheus node_exporter grafana 其他的选择安装 环境和版本号 系统: CentOS 7.9 prometheus: 2.54.1 pushgateway: 1.9.0 node_exporter: 1.8.2 alertmanager: 0.27.0 grafana:11.2.0 官网:https://prometheus.io/ 下载地址:h…...
Apache SeaTunnel Committer 进阶指南
Apache SeaTunnel 作为一个开源的数据集成工具,旨在简化和加速海量数据的采集和传输。 社区的 Committer 是指拥有项目存储库的写权限的社区成员,即 Committer 可以自行修改代码、文档和网站,也可以合并其他成员的贡献。成为 Apache SeaTunn…...
中南大学无人机智能体的全面评估!BEDI:用于评估无人机上具身智能体的综合性基准测试
作者:Mingning Guo, Mengwei Wu, Jiarun He, Shaoxian Li, Haifeng Li, Chao Tao单位:中南大学地球科学与信息物理学院论文标题:BEDI: A Comprehensive Benchmark for Evaluating Embodied Agents on UAVs论文链接:https://arxiv.…...
【JVM】- 内存结构
引言 JVM:Java Virtual Machine 定义:Java虚拟机,Java二进制字节码的运行环境好处: 一次编写,到处运行自动内存管理,垃圾回收的功能数组下标越界检查(会抛异常,不会覆盖到其他代码…...
【项目实战】通过多模态+LangGraph实现PPT生成助手
PPT自动生成系统 基于LangGraph的PPT自动生成系统,可以将Markdown文档自动转换为PPT演示文稿。 功能特点 Markdown解析:自动解析Markdown文档结构PPT模板分析:分析PPT模板的布局和风格智能布局决策:匹配内容与合适的PPT布局自动…...
ETLCloud可能遇到的问题有哪些?常见坑位解析
数据集成平台ETLCloud,主要用于支持数据的抽取(Extract)、转换(Transform)和加载(Load)过程。提供了一个简洁直观的界面,以便用户可以在不同的数据源之间轻松地进行数据迁移和转换。…...
unix/linux,sudo,其发展历程详细时间线、由来、历史背景
sudo 的诞生和演化,本身就是一部 Unix/Linux 系统管理哲学变迁的微缩史。来,让我们拨开时间的迷雾,一同探寻 sudo 那波澜壮阔(也颇为实用主义)的发展历程。 历史背景:su的时代与困境 ( 20 世纪 70 年代 - 80 年代初) 在 sudo 出现之前,Unix 系统管理员和需要特权操作的…...
UR 协作机器人「三剑客」:精密轻量担当(UR7e)、全能协作主力(UR12e)、重型任务专家(UR15)
UR协作机器人正以其卓越性能在现代制造业自动化中扮演重要角色。UR7e、UR12e和UR15通过创新技术和精准设计满足了不同行业的多样化需求。其中,UR15以其速度、精度及人工智能准备能力成为自动化领域的重要突破。UR7e和UR12e则在负载规格和市场定位上不断优化…...
Maven 概述、安装、配置、仓库、私服详解
目录 1、Maven 概述 1.1 Maven 的定义 1.2 Maven 解决的问题 1.3 Maven 的核心特性与优势 2、Maven 安装 2.1 下载 Maven 2.2 安装配置 Maven 2.3 测试安装 2.4 修改 Maven 本地仓库的默认路径 3、Maven 配置 3.1 配置本地仓库 3.2 配置 JDK 3.3 IDEA 配置本地 Ma…...
API网关Kong的鉴权与限流:高并发场景下的核心实践
🔥「炎码工坊」技术弹药已装填! 点击关注 → 解锁工业级干货【工具实测|项目避坑|源码燃烧指南】 引言 在微服务架构中,API网关承担着流量调度、安全防护和协议转换的核心职责。作为云原生时代的代表性网关,Kong凭借其插件化架构…...
ArcPy扩展模块的使用(3)
管理工程项目 arcpy.mp模块允许用户管理布局、地图、报表、文件夹连接、视图等工程项目。例如,可以更新、修复或替换图层数据源,修改图层的符号系统,甚至自动在线执行共享要托管在组织中的工程项。 以下代码展示了如何更新图层的数据源&…...
数据结构:泰勒展开式:霍纳法则(Horner‘s Rule)
目录 🔍 若用递归计算每一项,会发生什么? Horners Rule(霍纳法则) 第一步:我们从最原始的泰勒公式出发 第二步:从形式上重新观察展开式 🌟 第三步:引出霍纳法则&…...
