现象
在 WebSecurityConfigurerAdapter 配置了如下代码:
// 自定义未授权和未登录异常
http.exceptionHandling()
.accessDeniedHandler(new RestAccessDeniedHandler())
.authenticationEntryPoint(new RestAuthenticationEntryPoint());
在 Controller 层 REST 接口中添加有 @PreAuthorize 注解:
@PreAuthorize(value = "hasAuthority('Users.Update')")
@GetMapping("/hello")
public ResponseEntity<?> hello(@RequestParam(value = "name", required = false, defaultValue = "Tom") String name) {
return ResponseEntity.ok(RestResponse.buildResponse("Hi: " + name));
}
访问接口 /hello,报服务端 500 错误,没有执行我们设置的 accessDeniedHandler 来处理权限不足的异常。
原因
@PreAuthorize 注解的异常,抛出 AccessDeniedException 异常,不会被 accessDeniedHandler 捕获,而是会被全局异常捕获。全局异常处理 AccessDeniedException 的相关示例代码:
@Slf4j
@RestControllerAdvice
public class GlobalExceptionHandler {
@ExceptionHandler(Exception.class)
public ResponseEntity<RestResponse<Object>> handleException(Exception exception) {
String message = exception.getLocalizedMessage();
log.error("全局异常捕获Exception:{}", message, exception);
HttpStatus httpStatus = HttpStatus.INTERNAL_SERVER_ERROR;
if (exception instanceof BadCredentialsException) {
httpStatus = HttpStatus.UNAUTHORIZED;
}
if (exception instanceof HttpRequestMethodNotSupportedException) {
httpStatus = HttpStatus.METHOD_NOT_ALLOWED;
}
return RestResponse.buildError(httpStatus, message);
}
@ExceptionHandler(CommonException.class)
public ResponseEntity<RestResponse<Object>> handleException(CommonException exception) {
String message = exception.getLocalizedMessage();
log.error("全局异常捕获CommonException:{}", message);
return RestResponse.buildError(exception.getBusinessStatus(), message);
}
@ExceptionHandler(AccessDeniedException.class)
public ResponseEntity<RestResponse<Object>> handleException(AccessDeniedException exception) {
String message = exception.getLocalizedMessage();
log.error("全局异常捕获AccessDeniedException:{}", message);
return RestResponse.buildError(HttpStatus.FORBIDDEN, Forbidden);
}
}
如果需要被 accessDeniedHandler 捕获处理,则需要这么写 WebSecurityConfigurerAdapter 的代码:
http.cors().and()
.authorizeRequests().antMatchers("/hello0").permitAll()
// 注意hasRole、hasAuthority 如果出现异常,会调用设置的 accessDeniedHandler 方法
.antMatchers("/hello").hasAuthority("Users.Update")
.anyRequest().authenticated();
// 自定义未授权和未登录异常
http.exceptionHandling()
.accessDeniedHandler(new RestAccessDeniedHandler())
.authenticationEntryPoint(new RestAuthenticationEntryPoint());