Spring Security 自定义异常失效?源码分析与解决方案

🚀 作者主页: 有来技术
🔥 开源项目: youlai-mall 🍃 vue3-element-admin 🍃 youlai-boot
🌺 仓库主页: Gitee 💫 Github 💫 GitCode
💖 欢迎点赞 👍 收藏 ⭐留言 📝 如有错误敬请纠正!

目录

  • 问题描述
  • 项目关键代码
    • 自定义异常处理器
    • Spring Security 配置
    • 全局异常处理器
    • 访问权限测试接口
  • 问题分析
  • 解决方案
  • 源码阅读
    • ExceptionTranslationFilter#doFilter
    • DispatcherServlet#doDispatch
    • DispatcherServlet#processHandlerException
    • ExceptionHandlerExceptionResolver
  • 结语
  • 参考

问题描述

在基于 Spring Boot 3 + Spring Security 6 的权限管理系统开源项目 youlai-boot 中,根据官方提供的思路重写 AccessDeniedHandler 实现自定义异常处理,但发现该实现并没有生效,而是被全局异常捕获。期望的响应是 {"code":"A0301","msg":"访问未授权"},但实际上获得的响应与期望的不符,具体响应如下图所示:

项目关键代码

下面贴出关键代码,完整代码:youlai-boot

自定义异常处理器

package com.youlai.system.core.security.exception;/*** Spring Security 访问异常处理器** @author haoxr* @since 2022/10/18*/
@Component
public class MyAccessDeniedHandler implements AccessDeniedHandler {@Overridepublic void handle(HttpServletRequest request, HttpServletResponse response, AccessDeniedException accessDeniedException) throws IOException {ResponseUtils.writeErrMsg(response, ResultCode.ACCESS_UNAUTHORIZED);}
}

Spring Security 配置

package com.youlai.system.core.security.config;/*** Spring Security 权限配置** @author haoxr* @since 2023/2/17*/
@Configuration
@EnableWebSecurity
@EnableMethodSecurity
@RequiredArgsConstructor
public class SecurityConfig {private final MyAuthenticationEntryPoint authenticationEntryPoint;private final MyAccessDeniedHandler accessDeniedHandler;@Beanpublic SecurityFilterChain securityFilterChain(HttpSecurity http) throws Exception {http// ....exceptionHandling(httpSecurityExceptionHandlingConfigurer ->httpSecurityExceptionHandlingConfigurer.authenticationEntryPoint(authenticationEntryPoint) // 未认证异常处理.accessDeniedHandler(accessDeniedHandler) // 未授权访问异常处理)// ...;return http.build();}}

全局异常处理器

package com.youlai.system.common.exception;/*** 全局系统异常处理器**/
@RestControllerAdvice
@Slf4j
public class GlobalExceptionHandler {// ...// 捕获 Exception@ExceptionHandler(Exception.class)@ResponseStatus(HttpStatus.BAD_REQUEST)public <T> Result<T> handleException(Exception e) {log.error("unknown exception: {}", e.getMessage());return Result.failed(e.getLocalizedMessage());}}

访问权限测试接口

在删除角色接口中,使用了 Spring Security 提供的权限控制注解 @PreAuthorize 来进行权限校验。使用该注解可以在方法级别上对用户的权限进行校验,只有具有相应权限的用户才能执行该方法,否则会提示用户无权访问。

@Operation(summary = "删除角色")
@DeleteMapping("/{ids}")
@PreAuthorize("@ss.hasPerm('sys:role:delete')")
public Result deleteRoles(@Parameter(description ="删除角色,多个以英文逗号(,)分割") @PathVariable String ids
) {boolean result = roleService.deleteRoles(ids);return Result.judge(result);
}

问题分析

现象:当存在全局异常处理器时,自定义的 Spring Security 权限异常拦截处理器失效;而当移除全局异常处理器时,自定义的权限异常拦截处理器将正常生效。

猜测:全局异常处理器会干扰到 Spring Security 的权限异常处理流程,导致自定义的处理器无法按预期执行。

根据猜测,直接定位 Spring Security 异常处理和转换的过滤器 ExceptionTranslationFilterdoFilter 方法。

通过分析代码逻辑,可以确定问题的根源在于无权限访问异常被全局异常处理器捕获并处理掉了,因此不会进入 catch 分支执行 handleSpringSecurityException 方法。具体执行过程放在下文的源码解析章节。

解决方案

为了确保异常能够传递给 Spring Security 的自定义异常处理器,你可以对全局异常处理器(GlobalExceptionHandler)进行修改。如果异常是 AuthenticationExceptionAccessDeniedException,则继续将其抛出以便交给自定义处理器处理。

package com.youlai.system.common.exception;//.../*** 全局系统异常处理器**/
@RestControllerAdvice
@Slf4j
public class GlobalExceptionHandler {@ExceptionHandler(Exception.class)@ResponseStatus(HttpStatus.BAD_REQUEST)public <T> Result<T> handleException(Exception e) throws Exception{// 将 Spring Security 异常继续抛出,以便交给自定义处理器处理if (e instanceof AccessDeniedException|| e instanceof AuthenticationException) {throw e; }log.error("unknown exception: {}", e.getMessage());return Result.failed(e.getLocalizedMessage());}
}

通过这样的修改,当遇到 AuthenticationException 或者 AccessDeniedException 异常时,它们将被继续抛出,从而能够进入到 Spring Security 的自定义异常处理器进行处理。其他类型的异常仍然会在当前异常处理逻辑中进行处理。

修改后,按照自定义异常处理的输出,测试正常。

源码阅读

接下来通过源码阅读分析:为什么在存在全局异常处理器的情况下,Spring Security的无权限访问异常无法被自定义异常处理器处理?

ExceptionTranslationFilter#doFilter

在没有全局异常处理器的情况下,异常没有被拦截处理,会进入 catch 分支,由自定义异常处理器处理。

但如果存在全局异常处理器,则不会抛出异常,也就不会进入 catch 分支。

因此,需要进一步探究 chain.doFilter(request, response) 的后续处理,以了解为什么在有全局异常处理器的情况下没有异常抛出。

DispatcherServlet#doDispatch

DispatcherServlet#processHandlerException

DispatcherServlet#processDispatchResult 调用 DispatcherServlet#processHandlerException 方法处理异常

有无全局异常处理器在这个方法可以体现出区别了。

  • 有全局异常处理器

  • 无全局异常处理器

    image-20231130165031428

全局异常处理器 得到 exMv 不为 null ,后续不抛出异常,所以需要在 exMv = resolver.resolveException(request, response, handler, ex); 断点看下里面的逻辑。

ExceptionHandlerExceptionResolver

调用栈 HandlerExceptionResolverComposite#resolveException → AbstractHandlerExceptionResolver#resolveException → ExceptionHandlerExceptionResolver#doResolveHandlerMethodException

  • 有全局异常处理器

  • 无全局异常处理器

结语

在对异常处理流程进行详细的源码解读后,我们不仅解决了Spring Security权限异常处理在存在全局异常处理器时失效的问题,还对Spring MVC的异常处理机制有了更深入的了解。这一探索不仅有助于更好地理解框架的内部机制,也为未来的项目调优提供了有益的经验。

参考

  • https://stackoverflow.com/questions/31074040/custom-accessdeniedhandler-not-called
  • https://github.com/spring-projects/spring-security/issues/6908

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

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

相关文章

分享73个节日PPT,总有一款适合您

分享73个节日PPT&#xff0c;总有一款适合您 73个节日PPT下载 链接&#xff1a;https://pan.baidu.com/s/1FG9Y-9yR31Y-fs3zxKI4Pg?pwd6666 提取码&#xff1a;6666 Python采集代码下载链接&#xff1a;采集代码.zip - 蓝奏云 学习知识费力气&#xff0c;收集整理更不…

vue3 mixins

Vue3 中的 mixins 是一种复用组件逻辑的方式。在 Vue2 中&#xff0c;我们通常使用 mixins 来将一些全局的方法或者数据混入到多个组件中。但是在 Vue3 中&#xff0c;由于组合式 API 的引入&#xff0c;我们可以使用更灵活的方式来复用逻辑。 在 Vue3 中&#xff0c;我们可以…

LeetCode | 572. 另一棵树的子树

LeetCode | 572. 另一棵树的子树 OJ链接 我们需要判断两棵二叉树是否相同&#xff0c;如果再判断的的时候不同我们就直接返回false&#xff0c;否则就返回true然后再检查左子树和右子树里面是否存在subRoot子树~~ bool isSameTree(struct TreeNode* q, struct TreeNode* p) {…

C语言从入门到实战——常用内存函数的了解和模拟实现

常用内存函数的了解和实现 前言1. memcpy使用和模拟实现2. memmove使用和模拟实现3. memset函数的使用4. memcmp函数的使用 前言 内存函数&#xff08;memory functions&#xff09;指的是控制计算机内存操作的函数 1. memcpy使用和模拟实现 void * memcpy ( void * destinat…

uniapp:如何使用uCharts

目录 第一章 前言 第二章 安装插件uCharts 第三章 使用uCharts 第四章 注意 第一章 前言 需求&#xff1a;这是很久之前的一个项目的需求了&#xff0c;当时我刚接触app&#xff0c;有这么一个需求&#xff0c;在uniapp写的app项目中做一些图表统计&#xff0c;最开始以为…

EasyRecovery2024激活码秘钥

EasyRecovery从&#xff08;易恢复2024&#xff09;支持恢复不同存储介质数据&#xff0c;在Windows中恢复受损和删除文件,以及能检索数据格式化或损坏卷&#xff0c;甚至还可以从初始化磁盘。同时&#xff0c;你只需要最简单的操作就可以恢复数据文件&#xff0c;如&#xff1…

在java中如何解决in unnamed module @0x602ff1d9得问题

在日常得java开发中&#xff0c;点击maven得编译得时候会出现&#xff1a;class lombok.javac.apt.LombokProcessor (in unnamed module 0x58313b33) cannot access class com.sun.tools.javac.processing.JavacProcessingEnvironment (in module jdk.compiler) because module…

代码随想录算法训练营第五十四天 | 392.判断子序列,115.不同的子序列

目录 392.判断子序列 115.不同的子序列 392.判断子序列 题目链接&#xff1a;392. 判断子序列 设 s 的指针&#xff0c;遍历 t 的各个元素&#xff0c;当 t 与 s 对应元素相同时&#xff0c;指针前进&#xff1a; class Solution { public:bool isSubsequence(string s, string…

【刷题】Modular Multiplicative Inverse 模逆元

模逆元 定义 整数 a a a的模逆元是满足 a ⋅ x a\cdot x a⋅x模一个模数 m m m等于1。也就是找到一个数 x x x: a ⋅ x ≡ 1 mod m. a \cdot x \equiv 1 \text{ ~~~~mod m.} a⋅x≡1 mod m. 也可以把 x x x表示为 a − 1 a^{-1} a−1 需要注意模逆并不是总是存在。例如…

Vue3实现滚动到容器底部时发送请求,加载新数据

问题来源 在项目中出现了需要在容器滚动到底部时&#xff0c;加载新的数据的需求&#xff0c;以下是解决的方案笔记 解决 画了个流程图&#xff1a; 如图&#xff0c;先添加一个动态加载的图标&#xff0c;还有全部数据载完的《到底啦...》 大概这么个样子&#xff0c;之后呢…

PMP备考必看|浅谈PMP证书的价值,PMP考试详细全流程

作为已经在项目管理领域摸爬滚打五年的资深项目经理&#xff0c;我可以诚实的告诉大家&#xff0c;在项目管理领域拥有丰富项目管理经验的人都知道&#xff0c;很多公司在发布招聘信息时都会要求申请者持有PMP证书&#xff0c;这些证书在项目经理岗位的要求中经常出现。 在实际…

【PyTorch】softmax回归

文章目录 1. 模型与代码实现1.1. 模型1.2. 代码实现 2. Q&A 1. 模型与代码实现 1.1. 模型 背景 在分类问题中&#xff0c;模型的输出层是全连接层&#xff0c;每个类别对应一个输出。我们希望模型的输出 y ^ j \hat{y}_j y^​j​可以视为属于类 j j j的概率&#xff0c;然…

阿里云ACE认证含金量有多高?2023年海南E类人才认证政策告诉你答案!

2023年海南省高层次人才享受什么待遇&#xff1f;海南高层次人才住房补贴多少钱&#xff1f;海南高层次E类人才待遇有哪些&#xff1f;什么是海南高层次E类人才&#xff1f;E类人才怎么申请&#xff1f;这篇文章给大家详细介绍一下。 1.E类人才在海南有什么好处&#xff1f; …

ArcGIS提示当前许可不支持影像服务器

1、问题&#xff1a; 在用ArcGIS上处理影像栅格数据时&#xff08;比如栅格数据集裁剪、镶嵌数据集构建镶嵌线等&#xff09;经常会出现。 无法启动配置 RasterComander.ImageServer <详信息 在计算机XXXXX上创建服务器对象实例失败 当前许可不支持影像服务器。 ArcGIS提示当…

Python的模块与库,及if __name__ == ‘__main__语句【侯小啾Python基础领航计划 系列(二十四)】

Python的模块与库,及if name == ‘__main__语句【侯小啾Python基础领航计划 系列(二十四)】 大家好,我是博主侯小啾, 🌹꧔ꦿ🌹꧔ꦿ🌹꧔ꦿ🌹꧔ꦿ🌹꧔ꦿ🌹꧔ꦿ🌹꧔ꦿ🌹꧔ꦿ🌹꧔ꦿ🌹꧔ꦿ🌹꧔ꦿ🌹꧔ꦿ🌹꧔ꦿ🌹꧔ꦿ🌹꧔ꦿ🌹꧔ꦿ🌹꧔ꦿ🌹꧔…

轻量级网络结构的目标检测算法——Yolov8介绍

1. Yolov8算法概述 Yolov8是一种目标检测算法&#xff0c;它通过独特的双路径预测和紧密的连接的卷积网络进行目标检测。该算法采用了轻量级网络结构&#xff0c;同时保持了较高的性能&#xff0c;因此具有高效的特点。此外&#xff0c;Yolov8还采用了级联和金字塔…

MYSQL练题笔记-聚合函数-各赛事的用户注册率

一、题目相关内容 1&#xff09;相关的表 2&#xff09;题目 3&#xff09;帮助理解题目的示例&#xff0c;提供返回结果的格式 二、自己初步的理解 有两张不同左右的表&#xff0c;用户表和赛事注册表。然后解题。 1.各种赛事的用户注册百分率 各种赛事的意味着通过contes…

synchronized底层原理(一)

文章目录 1. 问题引入2. 相关概念3. Synchronized使用4. Synchronized底层原理1. 简介2. Monitor&#xff08;管程/监视器&#xff09;3. Java语言的内置管程synchronized4. Java对象的内存布局5. 如何使用MarkWord记录锁状态6. 偏向锁7. 轻量级锁 1. 问题引入 假设我们有1000…

手把手教你写一个Shell脚本部署你的服务

我们都知道&#xff0c;在开发的过程中&#xff0c;有很多部署自己微服务的方式&#xff0c;其中有各种各样的不同操作&#xff0c;比如使用 docker 打包为镜像的方式&#xff0c;还有基础使用 jar 包的方式进行部署&#xff0c;但是呢&#xff1f;使用 jar 包部署&#xff0c;…

XIAO ESP32S3之AI教程

一、sipeed AI教程 AI 指南 - Sipeed Wiki 二、TinyMX TinyMaix是国内sipeed团队面向单片机的超轻量级的神经网络推理库&#xff0c;即TinyML推理库&#xff0c;可以让你在任意单片机上运行轻量级深度学习模型。 英文:https://github.com/sipeed/TinyMaix 中文:https://gi…