当前位置: 首页 > news >正文

SpringSecurity源码学习二:异常处理

目录

  • 1. 原理
  • 2. 组件
  • 3. ExceptionTranslationFilter
    • 3.1 默认过滤器顺序
    • 3.2 ExceptionTranslationFilter源码
      • 3.2.1 AuthenticationException异常
      • 3.2.2 AccessDeniedException异常
  • 总结

1. 原理

Spring Security 异常处理的原理是通过一系列的异常处理器来处理在安全验证和授权过程中可能出现的异常。当发生异常时,Spring Security会根据异常的类型和配置的处理器来确定如何处理异常。

异常处理的原理可以概括为以下几个步骤:

  1. 认证过程中的异常处理:在用户进行身份验证时,可能会发生各种异常,例如用户名或密码错误、账户锁定等。Spring Security使用AuthenticationManager来处理认证过程中的异常,根据异常类型和配置的AuthenticationProvider来确定如何处理异常。

  2. 授权过程中的异常处理:在用户通过认证后,访问受保护资源时可能会发生授权异常,例如用户没有足够的权限访问资源。Spring Security使用AccessDecisionManager来处理授权过程中的异常,根据异常类型和配置的AccessDecisionVoter来确定如何处理异常。

  3. 异常转换和处理:Spring Security还提供了ExceptionTranslationFilter来处理身份验证和授权过程中的异常。该过滤器会捕获异常,并根据异常类型和配置的处理器,例如AuthenticationEntryPoint和AccessDeniedHandler,来进行适当的转换和处理。AuthenticationEntryPoint用于处理未经身份验证的请求,AccessDeniedHandler用于处理已经身份验证但无权访问资源的请求。

2. 组件

在Spring Security中,异常处理通常涉及以下几个关键组件:

  1. AuthenticationEntryPoint(认证入口点):用于处理未经身份验证的请求。当用户尝试访问受保护的资源时,如果尚未进行身份验证,AuthenticationEntryPoint将负责返回相应的响应,例如要求用户进行身份验证或返回错误消息。

  2. AccessDeniedHandler(访问拒绝处理器):用于处理已经身份验证但无权访问受保护资源的请求。如果用户已经通过身份验证,但没有足够的权限访问某个资源,AccessDeniedHandler将负责返回相应的响应,例如返回自定义的错误页面或错误消息。

  3. ExceptionTranslationFilter(异常转换过滤器):作为过滤器链中的一个关键组件,ExceptionTranslationFilter负责捕获Spring Security中的异常,并将其转换为适当的响应。它使用AuthenticationEntryPoint和AccessDeniedHandler来处理不同类型的异常,并提供合适的响应。

通过配置这些组件,您可以自定义异常处理的行为,以满足您的应用程序需求。您可以指定自定义的AuthenticationEntryPoint和AccessDeniedHandler,并将它们与Spring Security的过滤器链进行集成,以实现自定义的异常处理逻辑。

3. ExceptionTranslationFilter

当身份验证失败或访问被拒绝时,ExceptionTranslationFilter会拦截相应的异常,并根据异常的类型和配置的处理器来进行适当的处理。它根据异常类型分别使用AuthenticationEntryPoint和AccessDeniedHandler来处理不同的异常情况。

3.1 默认过滤器顺序

过滤器链在初始化的时候会默认加载好一些默认的过滤器,其中就包括ExceptionTranslationFilter过滤器。ExceptionTranslationFilter在过滤器链倒数第二个,最后一个是FilterSecurityInterceptor。FilterSecurityInterceptor是用来获取所有配置资源的访问授权信息,根据SecurityContextHolder中存储的用户信息来决定其是否有权限。

3.2 ExceptionTranslationFilter源码

	//问拒绝处理 就是你要访问某个资源,但是当你没有访问权限时,就会抛出异常,在此类中进行处理。private AccessDeniedHandler accessDeniedHandler = new AccessDeniedHandlerImpl();//顾名思义身份验证入口,主要用来判断你的身份,凡是在身份认证过程中发生的错误private AuthenticationEntryPoint authenticationEntryPoint;//用以判断SecurityContextHolder中所存储信息 判断上下文中有无用户信息来抛出异常private AuthenticationTrustResolver authenticationTrustResolver = new AuthenticationTrustResolverImpl();

这是此类中的三个比较重要的私有字段,其中accessDeniedHandler用来处理权限异常;authenticationEntryPoint用来处理认证异常;authenticationTrustResolver用以判断SecurityContextHolder中所存储信息 判断上下文中有无用户信息来抛出异常。其中accessDeniedHandler和authenticationEntryPoint异常处理在ExceptionTranslationFilter过滤器中处理。

	private void doFilter(HttpServletRequest request, HttpServletResponse response, FilterChain chain)throws IOException, ServletException {try {//执行下一个过滤器逻辑,也就是SecurityContextHolder过滤器。chain.doFilter(request, response);}catch (IOException ex) {throw ex;}catch (Exception ex) {// Try to extract a SpringSecurityException from the stacktrace//获取全部异常Throwable[] causeChain = this.throwableAnalyzer.determineCauseChain(ex);//判断异常的类型 是否为AuthenticationExceptionRuntimeException securityException = (AuthenticationException) this.throwableAnalyzer.getFirstThrowableOfType(AuthenticationException.class, causeChain);if (securityException == null) {//判断是否为AccessDeniedExceptionsecurityException = (AccessDeniedException) this.throwableAnalyzer.getFirstThrowableOfType(AccessDeniedException.class, causeChain);}//如果不是上边两个异常,继续抛出,说明此过滤器只处理AuthenticationException和AccessDeniedException异常if (securityException == null) {rethrow(ex);}if (response.isCommitted()) {throw new ServletException("Unable to handle the Spring Security Exception "+ "because the response is already committed.", ex);}//处理异常handleSpringSecurityException(request, response, chain, securityException);}}

chain.doFilter(request, response)会执行执行下一个过滤器逻辑,也就是SecurityContextHolder过滤器。当用户未认证或者接口未授权时,会被catch到执行异常处理。处理报错的核心逻辑在handleSpringSecurityException()方法中。

	private void handleSpringSecurityException(HttpServletRequest request, HttpServletResponse response,FilterChain chain, RuntimeException exception) throws IOException, ServletException {if (exception instanceof AuthenticationException) {//处理AuthenticationException异常handleAuthenticationException(request, response, chain, (AuthenticationException) exception);}else if (exception instanceof AccessDeniedException) {//处理AccessDeniedException异常handleAccessDeniedException(request, response, chain, (AccessDeniedException) exception);}}

异常分为两部分:handleAuthenticationException处理AuthenticationException异常,handleAccessDeniedException处理AccessDeniedException异常。

3.2.1 AuthenticationException异常

	private void handleAuthenticationException(HttpServletRequest request, HttpServletResponse response,FilterChain chain, AuthenticationException exception) throws ServletException, IOException {this.logger.trace("Sending to authentication entry point since authentication failed", exception);sendStartAuthentication(request, response, chain, exception);}protected void sendStartAuthentication(HttpServletRequest request, HttpServletResponse response, FilterChain chain,AuthenticationException reason) throws ServletException, IOException {// SEC-112: Clear the SecurityContextHolder's Authentication, as the// existing Authentication is no longer considered validSecurityContextHolder.getContext().setAuthentication(null);this.requestCache.saveRequest(request, response);//调用authenticationEntryPoint子类的commence方法  我们自定义authenticationEntryPoint的子类this.authenticationEntryPoint.commence(request, response, reason);}

可以看到,最终会调用AuthenticationEntryPoint的commence方法。AuthenticationEntryPoint是一个接口,有默认的实现类,同时我们也可以自定义authenticationEntryPoint。

自定义authenticationEntryPoint

public class MyAuthenticationEntryPoint implements AuthenticationEntryPoint {@Overridepublic void commence(HttpServletRequest request,HttpServletResponse response,AuthenticationException authException) throws IOException {// 用户自定义返回}
}

3.2.2 AccessDeniedException异常

	private void handleAccessDeniedException(HttpServletRequest request, HttpServletResponse response,FilterChain chain, AccessDeniedException exception) throws ServletException, IOException {Authentication authentication = SecurityContextHolder.getContext().getAuthentication();//是否是游客登录boolean isAnonymous = this.authenticationTrustResolver.isAnonymous(authentication);//如果是游客登录或者是rememberMe用户,则重新登录if (isAnonymous || this.authenticationTrustResolver.isRememberMe(authentication)) {if (logger.isTraceEnabled()) {logger.trace(LogMessage.format("Sending %s to authentication entry point since access is denied",authentication), exception);}//重新登录sendStartAuthentication(request, response, chain,new InsufficientAuthenticationException(this.messages.getMessage("ExceptionTranslationFilter.insufficientAuthentication","Full authentication is required to access this resource")));}else {if (logger.isTraceEnabled()) {logger.trace(LogMessage.format("Sending %s to access denied handler since access is denied", authentication),exception);}//执行我们的权限认证错误自定义逻辑this.accessDeniedHandler.handle(request, response, exception);}}

此代码最终调用的是AccessDeniedHandler的handle方法,同样的AccessDeniedHandler是接口,同时有默认实现类,也可以自定义实现。

自定义AccessDeniedHandler

public class MyAccessDeniedHandler implements AccessDeniedHandler {@Overridepublic void handle(HttpServletRequest request, HttpServletResponse response, AccessDeniedException accessDeniedException) throws IOException {//用户自定义实现}
}

总结

  1. 首先,ExceptionTranslationFilter 调用 FilterChain.doFilter(request, response) 来调用应用程序的其他过滤器。

  2. 如果用户没有被认证,或者是一个 AuthenticationException,那么就开始认证,SecurityContextHolder 被清理掉。HttpServletRequest 被保存起来,这样一旦认证成功,它就可以用来重放原始请求。
    AuthenticationEntryPoint 用于请求客户的凭证。例如,它可以重定向到一个登录页面或发送一个 WWW-Authenticate 头。

  3. 如果是 AccessDeniedException,那么就是 Access Denied。AccessDeniedHandler 被调用来处理拒绝访问(access denied)。

相关文章:

SpringSecurity源码学习二:异常处理

目录 1. 原理2. 组件3. ExceptionTranslationFilter3.1 默认过滤器顺序3.2 ExceptionTranslationFilter源码3.2.1 AuthenticationException异常3.2.2 AccessDeniedException异常 总结 1. 原理 Spring Security 异常处理的原理是通过一系列的异常处理器来处理在安全验证和授权过…...

代码随想录算法训练营第23期day28|491.递增子序列 46.全排列 47.全排列 II

目录 一、(leetcode 491)递增子序列 二、(leetcode 46)全排列 三、(leetcode 47)全排列 II 一、(leetcode 491)递增子序列 力扣题目链接 状态:去重方法错误。 这道题…...

ubuntu磁盘扩容

1、参考链接: https://blog.csdn.net/qq_43265072/article/details/112312223 2、尝试过程中小心翼翼,生怕待会系统崩掉,要重装。。。 不过呢有撤销和提交按钮就非常贴心,如果稍有不慎就一路回撤就好啦 说一下怎么移动空间&…...

C/S架构学习之使用select实现TCP小型并发服务器

select实现TCP小型并发服务器的流程:一、创建套接字(socket函数):通信域选择IPV4网络协议、套接字类型选择流式; int sockfd socket(AF_INET,SOCK_STREAM,0); //通信域选择IPV4、套接字类型选择流式二、填充服务器的网…...

公司注册类型分类标准是怎样的

公司法上的分支机构、分公司、子公司是什么 - 公司法 (一)以公司股东的责任范围为标准分类 以公司股东的责任范围为标准,亦即以公司股东是否对公司债务承担责任为标准,可将公司分为无限责任公司、两合公司、股份两合公司、股份有限公司和有限责任公司。…...

5.MidBook项目经验之MongoDB,Nacos,网关

1.医院查询接口 //系统1(signsignMD5加密后) ----> 系统2(数据库signMD5加密 相对比),好处在于网络之间传输不会得到直接得到sign 2.上传和删除科室信息 //map转jsonString,然后再转为对象//保存需要查数据库是否存在,存在修改,不存在添加//接口的包引入不对导致调用引包错误…...

XMLHttpRequest对象的Get请求和Post请求的用法

XMLHttpRequest对象的Get请求和Post请求的用法 Get请求提交数据 <!DOCTYPE html> <html lang"en"> <head><meta charset"UTF-8"><title>发送ajax get请求</title> </head> <body> <script type"…...

Tomcat动静分离

访问静态页面和访问动态页面分开&#xff0c;实现动态页面和静态页面的负载均衡 一、七层动静分离 3台虚拟机 1、nginx1既是代理也是静态 nginx1&#xff1a;20.0.0.11 2、请求动态页面&#xff1a;Tomcat1和Tomcat2 Tomcat1&#xff1a;20.0.0.31 Tomcat2&#xff1a;20…...

一些ECharts配置

基于vue3&#xff0c;EChart5.4.3版本 Line <script setup lang"ts"> import {onBeforeUnmount, onMounted, ref, watch} from "vue" import {useEcharts, type ECOption} from "/composables" import * as echarts from "echarts/c…...

C调用Objective-C的类和方法

C调用Objective-C的类和方法 最近有一个C提供回调接口调用Objective-c接口来传递数据的需求&#xff0c;研究了很久&#xff0c;最终通过bing的AI对话查到了需要的内容&#xff0c;这里记录一下&#xff0c;算是给基于C的IOS开发开了一个头。 在Objective-C中&#xff0c;你可…...

驱动开发day1

头文件 #ifndef __HEAD_H__ #define __HEAD_H__ #define PHY_LED1_MODER 0X50006000 #define PHY_LED1_ODR 0X50006014#define PHY_LED2_MODER 0x50007000 #define PHY_LED2_ODR 0x50007014#define PHY_LED3_MODER 0x50006000 #define PHY_LED3_ODR 0x50006014#define P…...

C++ linux vscode编译

.cpp .h文件关系与编译命令 单一cpp文件编译多个.cpp文件编译.h头文件和.cpp源文件在同一目录下编译.h头文件和.cpp源文件在不同一目录下编译 单一cpp文件编译 //test.cpp为测试源文件&#xff0c; a.out为输出可执行文件 g test.cpp -o a.out多个.cpp文件编译 目录1结构如下…...

卷积神经网络CNN学习笔记

目录 1.全连接层存在的问题2.卷积运算3.填充(padding)3.1填充(padding)的意义 4.步幅(stride)5.三维数据的卷积运算6.结合方块思考7.批处理8.conv2d代码参考文章 1.全连接层存在的问题 在全连接层中&#xff0c;相邻层的神经元全部连接在一起&#xff0c;输出的数量可以任意决…...

Java的Socket Timeout和tcp的存活探测包是不是一个东西

背景 你有没有好奇过我们在java中通过Socket.setSoTimeout()设置timeout参数时&#xff0c;他怎么做到在timeout时间到了之后连接就报错的&#xff1f;有没有产生过误解&#xff0c;这个参数就是设置keepalive探测包的检测间隔&#xff1f; 问题真相 其实Socket.setSoTimeou…...

基于跳蛛优化的BP神经网络(分类应用) - 附代码

基于跳蛛优化的BP神经网络&#xff08;分类应用&#xff09; - 附代码 文章目录 基于跳蛛优化的BP神经网络&#xff08;分类应用&#xff09; - 附代码1.鸢尾花iris数据介绍2.数据集整理3.跳蛛优化BP神经网络3.1 BP神经网络参数设置3.2 跳蛛算法应用 4.测试结果&#xff1a;5.M…...

基于鹈鹕优化的BP神经网络(分类应用) - 附代码

基于鹈鹕优化的BP神经网络&#xff08;分类应用&#xff09; - 附代码 文章目录 基于鹈鹕优化的BP神经网络&#xff08;分类应用&#xff09; - 附代码1.鸢尾花iris数据介绍2.数据集整理3.鹈鹕优化BP神经网络3.1 BP神经网络参数设置3.2 鹈鹕算法应用 4.测试结果&#xff1a;5.M…...

『ARM』和『x86』处理器架构解析指南

前言 如果问大家是否知道 CPU&#xff0c;我相信不会得到否定的答案&#xff0c;但是如果继续问大家是否了解 ARM 和 X86 架构&#xff0c;他们的区别又是什么&#xff0c;相信可能部分人就会哑口无言了 目前随着深度学习、高性能计算、NLP、AIGC、GLM、AGI 的技术迭代&#…...

Android 13.0 系统设置 app详情页默认关闭流量数据的开关

1.概述 在13.0的系统产品开发中,移动流量消耗也是关于产品优化的一个方面,由于产品需求需要对app详情页的流量进行管控默认关闭流量开关,不让流量无故流失,所以需要从流量开关分析问题流量打开流程,然后关闭 2.系统设置 app详情页默认关闭流量数据的开关的核心类 package…...

054协同过滤算法的电影推荐系统

大家好✌&#xff01;我是CZ淡陌。一名专注以理论为基础实战为主的技术博主&#xff0c;将再这里为大家分享优质的实战项目&#xff0c;本人在Java毕业设计领域有多年的经验&#xff0c;陆续会更新更多优质的Java实战项目&#xff0c;希望你能有所收获&#xff0c;少走一些弯路…...

分享一个基于JavaWeb的私人牙科诊所预约挂号就诊系统的设计与实现项目源码调试 lw 开题 ppt

&#x1f495;&#x1f495;作者&#xff1a;计算机源码社 &#x1f495;&#x1f495;个人简介&#xff1a;本人七年开发经验&#xff0c;擅长Java、Python、PHP、.NET、微信小程序、爬虫、大数据等&#xff0c;大家有这一块的问题可以一起交流&#xff01; &#x1f495;&…...

智慧医疗能源事业线深度画像分析(上)

引言 医疗行业作为现代社会的关键基础设施,其能源消耗与环境影响正日益受到关注。随着全球"双碳"目标的推进和可持续发展理念的深入,智慧医疗能源事业线应运而生,致力于通过创新技术与管理方案,重构医疗领域的能源使用模式。这一事业线融合了能源管理、可持续发…...

JavaScript 中的 ES|QL:利用 Apache Arrow 工具

作者&#xff1a;来自 Elastic Jeffrey Rengifo 学习如何将 ES|QL 与 JavaScript 的 Apache Arrow 客户端工具一起使用。 想获得 Elastic 认证吗&#xff1f;了解下一期 Elasticsearch Engineer 培训的时间吧&#xff01; Elasticsearch 拥有众多新功能&#xff0c;助你为自己…...

从零实现富文本编辑器#5-编辑器选区模型的状态结构表达

先前我们总结了浏览器选区模型的交互策略&#xff0c;并且实现了基本的选区操作&#xff0c;还调研了自绘选区的实现。那么相对的&#xff0c;我们还需要设计编辑器的选区表达&#xff0c;也可以称为模型选区。编辑器中应用变更时的操作范围&#xff0c;就是以模型选区为基准来…...

Linux简单的操作

ls ls 查看当前目录 ll 查看详细内容 ls -a 查看所有的内容 ls --help 查看方法文档 pwd pwd 查看当前路径 cd cd 转路径 cd .. 转上一级路径 cd 名 转换路径 …...

Python实现prophet 理论及参数优化

文章目录 Prophet理论及模型参数介绍Python代码完整实现prophet 添加外部数据进行模型优化 之前初步学习prophet的时候&#xff0c;写过一篇简单实现&#xff0c;后期随着对该模型的深入研究&#xff0c;本次记录涉及到prophet 的公式以及参数调优&#xff0c;从公式可以更直观…...

Keil 中设置 STM32 Flash 和 RAM 地址详解

文章目录 Keil 中设置 STM32 Flash 和 RAM 地址详解一、Flash 和 RAM 配置界面(Target 选项卡)1. IROM1(用于配置 Flash)2. IRAM1(用于配置 RAM)二、链接器设置界面(Linker 选项卡)1. 勾选“Use Memory Layout from Target Dialog”2. 查看链接器参数(如果没有勾选上面…...

【JavaSE】绘图与事件入门学习笔记

-Java绘图坐标体系 坐标体系-介绍 坐标原点位于左上角&#xff0c;以像素为单位。 在Java坐标系中,第一个是x坐标,表示当前位置为水平方向&#xff0c;距离坐标原点x个像素;第二个是y坐标&#xff0c;表示当前位置为垂直方向&#xff0c;距离坐标原点y个像素。 坐标体系-像素 …...

QT: `long long` 类型转换为 `QString` 2025.6.5

在 Qt 中&#xff0c;将 long long 类型转换为 QString 可以通过以下两种常用方法实现&#xff1a; 方法 1&#xff1a;使用 QString::number() 直接调用 QString 的静态方法 number()&#xff0c;将数值转换为字符串&#xff1a; long long value 1234567890123456789LL; …...

浪潮交换机配置track检测实现高速公路收费网络主备切换NQA

浪潮交换机track配置 项目背景高速网络拓扑网络情况分析通信线路收费网络路由 收费汇聚交换机相应配置收费汇聚track配置 项目背景 在实施省内一条高速公路时遇到的需求&#xff0c;本次涉及的主要是收费汇聚交换机的配置&#xff0c;浪潮网络设备在高速项目很少&#xff0c;通…...

NXP S32K146 T-Box 携手 SD NAND(贴片式TF卡):驱动汽车智能革新的黄金组合

在汽车智能化的汹涌浪潮中&#xff0c;车辆不再仅仅是传统的交通工具&#xff0c;而是逐步演变为高度智能的移动终端。这一转变的核心支撑&#xff0c;来自于车内关键技术的深度融合与协同创新。车载远程信息处理盒&#xff08;T-Box&#xff09;方案&#xff1a;NXP S32K146 与…...