当前位置: 首页 > 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;&…...

OpenLayers 可视化之热力图

注&#xff1a;当前使用的是 ol 5.3.0 版本&#xff0c;天地图使用的key请到天地图官网申请&#xff0c;并替换为自己的key 热力图&#xff08;Heatmap&#xff09;又叫热点图&#xff0c;是一种通过特殊高亮显示事物密度分布、变化趋势的数据可视化技术。采用颜色的深浅来显示…...

python打卡day49

知识点回顾&#xff1a; 通道注意力模块复习空间注意力模块CBAM的定义 作业&#xff1a;尝试对今天的模型检查参数数目&#xff0c;并用tensorboard查看训练过程 import torch import torch.nn as nn# 定义通道注意力 class ChannelAttention(nn.Module):def __init__(self,…...

《从零掌握MIPI CSI-2: 协议精解与FPGA摄像头开发实战》-- CSI-2 协议详细解析 (一)

CSI-2 协议详细解析 (一&#xff09; 1. CSI-2层定义&#xff08;CSI-2 Layer Definitions&#xff09; 分层结构 &#xff1a;CSI-2协议分为6层&#xff1a; 物理层&#xff08;PHY Layer&#xff09; &#xff1a; 定义电气特性、时钟机制和传输介质&#xff08;导线&#…...

AtCoder 第409​场初级竞赛 A~E题解

A Conflict 【题目链接】 原题链接&#xff1a;A - Conflict 【考点】 枚举 【题目大意】 找到是否有两人都想要的物品。 【解析】 遍历两端字符串&#xff0c;只有在同时为 o 时输出 Yes 并结束程序&#xff0c;否则输出 No。 【难度】 GESP三级 【代码参考】 #i…...

UE5 学习系列(三)创建和移动物体

这篇博客是该系列的第三篇&#xff0c;是在之前两篇博客的基础上展开&#xff0c;主要介绍如何在操作界面中创建和拖动物体&#xff0c;这篇博客跟随的视频链接如下&#xff1a; B 站视频&#xff1a;s03-创建和移动物体 如果你不打算开之前的博客并且对UE5 比较熟的话按照以…...

(二)原型模式

原型的功能是将一个已经存在的对象作为源目标,其余对象都是通过这个源目标创建。发挥复制的作用就是原型模式的核心思想。 一、源型模式的定义 原型模式是指第二次创建对象可以通过复制已经存在的原型对象来实现,忽略对象创建过程中的其它细节。 📌 核心特点: 避免重复初…...

Springcloud:Eureka 高可用集群搭建实战(服务注册与发现的底层原理与避坑指南)

引言&#xff1a;为什么 Eureka 依然是存量系统的核心&#xff1f; 尽管 Nacos 等新注册中心崛起&#xff0c;但金融、电力等保守行业仍有大量系统运行在 Eureka 上。理解其高可用设计与自我保护机制&#xff0c;是保障分布式系统稳定的必修课。本文将手把手带你搭建生产级 Eur…...

【Web 进阶篇】优雅的接口设计:统一响应、全局异常处理与参数校验

系列回顾&#xff1a; 在上一篇中&#xff0c;我们成功地为应用集成了数据库&#xff0c;并使用 Spring Data JPA 实现了基本的 CRUD API。我们的应用现在能“记忆”数据了&#xff01;但是&#xff0c;如果你仔细审视那些 API&#xff0c;会发现它们还很“粗糙”&#xff1a;有…...

LLM基础1_语言模型如何处理文本

基于GitHub项目&#xff1a;https://github.com/datawhalechina/llms-from-scratch-cn 工具介绍 tiktoken&#xff1a;OpenAI开发的专业"分词器" torch&#xff1a;Facebook开发的强力计算引擎&#xff0c;相当于超级计算器 理解词嵌入&#xff1a;给词语画"…...

Axios请求超时重发机制

Axios 超时重新请求实现方案 在 Axios 中实现超时重新请求可以通过以下几种方式&#xff1a; 1. 使用拦截器实现自动重试 import axios from axios;// 创建axios实例 const instance axios.create();// 设置超时时间 instance.defaults.timeout 5000;// 最大重试次数 cons…...