Spring Boot中403 Forbidden自定义异常处理器的问题
问题描述
我用Spring Boot开发后端应用,采用Keycloak做用户与权限管理,目前在自定义403 Forbidden错误的异常处理器时遇到问题。
未配置全局异常处理器时,能针对未授权令牌返回正确的401和403响应,但我需要全局异常处理器处理意外错误,代码如下:
@ExceptionHandler(value = Exception.class) public ResponseEntity<String> generalExceptionHandler(Exception e) { log.error(e.getMessage()); return ResponseEntity.status(HttpStatus.INTERNAL_SERVER_ERROR).body("General Error"); }
如果不配置其他异常处理器,所有导致401或403响应的场景都会被这个全局处理器接管,不符合我的需求——我希望这两类错误能返回对应的正确消息。
于是我开发了针对AccessDeniedException的异常处理器:
@ExceptionHandler(value = AccessDeniedException.class) public ResponseEntity<String> accessDeniedExceptionHandler(Exception e) { log.error(e.getMessage()); return ResponseEntity.status(HttpStatus.UNAUTHORIZED).body("Access is denied"); }
现在所有401场景能被这个处理器正确处理,但403 Forbidden场景也会被它捕获,我需要区分这两类错误,为403单独设置处理器返回对应消息。
解决方案
核心问题是你混淆了401和403对应的异常类型:
- 401(未授权,比如令牌无效、过期)对应的是认证异常,属于
AuthenticationException的子类(Keycloak场景下常见的有InvalidTokenException、TokenExpiredException等) - 403(禁止访问,令牌有效但无对应权限)对应的才是
AccessDeniedException
你需要分别为这两类异常编写处理器,利用Spring异常处理器的优先级规则(优先匹配最具体的异常类型)来区分:
- 编写401对应的认证异常处理器:
@ExceptionHandler(value = AuthenticationException.class) public ResponseEntity<String> authenticationExceptionHandler(AuthenticationException e) { log.error(e.getMessage()); return ResponseEntity.status(HttpStatus.UNAUTHORIZED).body("无效或过期的令牌"); }
- 编写403对应的权限异常处理器:
@ExceptionHandler(value = AccessDeniedException.class) public ResponseEntity<String> accessDeniedExceptionHandler(AccessDeniedException e) { log.error(e.getMessage()); return ResponseEntity.status(HttpStatus.FORBIDDEN).body("你没有访问该资源的权限"); }
- 保留全局异常处理器(处理其他所有未捕获的意外错误):
@ExceptionHandler(value = Exception.class) public ResponseEntity<String> generalExceptionHandler(Exception e) { log.error(e.getMessage()); return ResponseEntity.status(HttpStatus.INTERNAL_SERVER_ERROR).body("General Error"); }
关键说明
Spring异常处理器会优先匹配最具体的异常类型,所以AuthenticationException和AccessDeniedException会被各自的处理器捕获,不会走到全局的Exception处理器。如果需要更精准匹配Keycloak的特定认证异常(比如TokenExpiredException),可以单独为该异常编写处理器,优先级会比AuthenticationException更高。
内容的提问来源于stack exchange,提问作者Reza Azad
相关产品推荐
相关产品推荐

