推荐抛出 SecurityException 的子类(如 InsufficientPermissionException),需自定义异常类、显式权限校验并统一处理,避免语义不清或静默失败。

Java 权限不足时,推荐抛出 SecurityException 的子类(而非直接 new SecurityException),这样语义更清晰、便于统一拦截和审计。关键在于:自定义异常类 + 明确的检查逻辑 + 合理的抛出时机。
定义自定义安全异常类
继承 SecurityException,添加构造方法支持消息、原因和权限上下文信息:
public class InsufficientPermissionException extends SecurityException {
private final String requiredPermission;
private final String subjectId;
public InsufficientPermissionException(String message, String requiredPermission) {
super(message);
this.requiredPermission = requiredPermission;
this.subjectId = null;
}
public InsufficientPermissionException(String message, String requiredPermission, String subjectId) {
super(message);
this.requiredPermission = requiredPermission;
this.subjectId = subjectId;
}
// 可选:提供 getter 方便日志或监控
public String getRequiredPermission() { return requiredPermission; }
public String getSubjectId() { return subjectId; }
}
在业务逻辑中主动检查并抛出
不要依赖 JVM 自动检查(如 SecurityManager 已基本弃用),而应在关键操作前显式校验权限,并按需抛出自定义异常:
你接手一个需求时,是不是经常这样:"改一个小字段 → 结果把不相关的代码也改了"、"需求说得很模糊 → 做着做着发现跑偏了"、"数据库加个字段 → 上线才发现漏了数据兼容"、"做了个新功能 → 一个月后自己都忘了当时怎么设计的"、"写到一半被中断 → 再回来不知道从哪继续";这个 Skill 就是专门解决这些问题的。 它会自动探测你的项目环境(JDK、Spring Boot、ORM、数据库、前端框架),记在
- 从当前上下文(如 Spring Security 的
SecurityContextHolder或自定义 ThreadLocal)获取用户/主体信息 - 调用权限服务(如
permissionService.hasPermission("user:delete"))判断是否具备所需权限 - 不满足时,构造带上下文信息的异常并 throw
public void deleteUser(Long userId) {
String currentUserId = getCurrentUserId(); // 如从 JWT 或 Session 获取
if (!permissionService.hasPermission(currentUserId, "user:delete")) {
throw new InsufficientPermissionException(
String.format("User %s lacks permission to delete user %d", currentUserId, userId),
"user:delete",
currentUserId
);
}
// 执行删除逻辑...
}
统一异常处理(可选但推荐)
在 Spring Boot 中,可用 @ControllerAdvice 捕获该异常并返回标准错误响应:
立即学习“Java免费学习笔记(深入)”;
@ExceptionHandler(InsufficientPermissionException.class)
public ResponseEntity<ErrorResponse> handleInsufficientPermission(
InsufficientPermissionException e) {
ErrorResponse error = new ErrorResponse(
"FORBIDDEN",
e.getMessage(),
HttpStatus.FORBIDDEN.value()
);
// 可记录审计日志:e.getRequiredPermission(), e.getSubjectId()
return ResponseEntity.status(HttpStatus.FORBIDDEN).body(error);
}
避免常见误区
- 不要用
IllegalArgumentException或RuntimeException替代——丢失安全语义 - 不要在 catch 块里“吞掉”原始
SecurityException后静默失败——应明确反馈 - 避免在工具类或底层框架层抛出——权限判断应靠近业务入口(如 Controller 或 Service 方法头)
- 日志中记录异常时,脱敏敏感字段(如用户 ID 可记为 hash 或掩码)

















