accessdeniedexception 分为 spring security 抛出的 org.springframework.security.access.accessdeniedexception 和 jdk 安全管理器抛出的 java.security.accesscontrolexception,二者机制不同,需通过异常全限定名和堆栈确认来源。

AccessDeniedException 是 SecurityManager 拦截的结果,不是权限配置问题
Java 的 AccessDeniedException(注意:它本身是 Spring Security 的异常,不是 JDK 原生类)常被误认为和文件权限、Linux 用户权限有关。实际上,在纯 Java SE 环境中,真正触发访问拒绝的是 SecurityManager 的检查机制——比如你调用 System.exit()、读写系统属性、反射访问私有成员等,而当前策略文件(java.policy)没授权,JVM 就会抛 AccessControlException(这才是 JDK 原生异常)。Spring 的 AccessDeniedException 是另一套逻辑,和 SecurityManager 无关。
所以第一步要确认:你看到的到底是 java.security.AccessControlException(JDK 安全管理器抛的),还是 org.springframework.security.access.AccessDeniedException(Spring Security 抛的)。两者处理路径完全不同。
- 查堆栈:看异常全限定名,重点看
Caused by:上层是否含sun.security或java.security - 如果项目没显式启用
SecurityManager(比如没加-Djava.security.manager参数),那基本可以排除它 - Spring Boot 默认不启用
SecurityManager,但可能因旧版依赖或容器(如 Tomcat 8 以前)默认加载
如何临时禁用 SecurityManager(仅用于排查)
如果你确认问题是 AccessControlException,且只是想快速验证是否为策略限制导致,最直接的方式是启动时绕过它:
- 启动参数加
-Djava.security.manager=(注意等号后为空),这会强制不加载任何安全管理器 - 或者更彻底:启动参数加
-Djava.security.manager=allow(部分 JDK 版本支持,但不可靠) - 不推荐删
java.security文件里的security.manager行——影响全局,且 JDK 17+ 已默认移除该机制
⚠️ 注意:SecurityManager 自 JDK 17 起已标记为 @Deprecated(forRemoval=true),JDK 21 彻底移除。如果你在 JDK 17+ 环境下还看到它生效,大概率是应用服务器(如 WebLogic)或测试框架(如 older PowerMock)自己注入的。
立即学习“Java免费学习笔记(深入)”;
Spring Security 的 AccessDeniedException 怎么真正在代码里捕获
Spring 的 AccessDeniedException 不是 JVM 抛的,而是 FilterSecurityInterceptor 在请求链路中主动 throw 的,所以不能靠 try-catch 普通方法调用捕获——它发生在 Spring MVC 的拦截器或 Filter 层。
- 全局处理:实现
AccessDeniedHandler接口,重写handle()方法,注册为@Bean - 别在 Controller 里 try-catch
AccessDeniedException—— 它根本不会传播到那里,除非你手动 throw - 若需运行时判断权限,用
SecurityContextHolder.getContext().getAuthentication()+authorizationManager.check()(Spring Security 6.1+) - 常见误操作:在
@PreAuthorize表达式里写错 SpEL,导致抛ExpressionEvaluationException,而非AccessDeniedException
示例注册方式:
@Bean
public AccessDeniedHandler accessDeniedHandler() {
return new CustomAccessDeniedHandler();
}
为什么本地跑得好,上线就 AccessDeniedException
环境差异往往藏在安全上下文传递和认证状态上,而不是代码本身。
- 反向代理(Nginx / ALB)没透传
X-Forwarded-For或Authorization头,导致 Spring Security 认为用户未认证,后续权限检查直接 fail - 使用了
SessionCreationPolicy.STATELESS,但前端没带 token,或 token 过期/签名无效,Authentication为 null,AccessDeniedException实际是“认证失败后误走授权流程”的副作用 - 多模块项目中,
spring-boot-starter-security被某个 starter 间接引入,但没配WebSecurityConfigurerAdapter(旧版)或SecurityFilterChain(新版),导致默认策略锁死所有端点 - JDK 版本升级后,
SecurityManager被静默忽略,但旧策略文件残留,误导你去查权限配置
真正难定位的,是那种没打日志、没进 handler、请求直接 403 且堆栈里连 Spring 类都没有的情况——大概率是网关层或容器层做的拦截,不是你的代码问题。










