项目使用 SpringSecurity 进行 API 权限控制,在项目中实现了 AccessDeniedHandler 接口用于被拒绝时的异常处理:
@Slf4j
@Component
public class MyAccessDeniedHandler implements AccessDeniedHandler {
@Override
public void handle(HttpServletRequest httpServletRequest, HttpServletResponse httpServletResponse, AccessDeniedException e) throws IOException, ServletException {
log.info("AccessDeniedHandler 异常....."); //打印被拒绝日志
httpServletResponse.setContentType("text/json;charset=utf-8");
httpServletResponse.getWriter().write(JSON.toJSONString(ResultGenerator.noPermission()));
}
}
并且在 WebSecurityConfigurerAdapter 的 http 配置中配置了异常处理:
@Bean
public AccessDeniedHandler getAccessDeniedHandler() {
return new MyAccessDeniedHandler();
}
@Override
protected void configure(HttpSecurity http) throws Exception {
// ....
//异常处理
http.exceptionHandling().accessDeniedHandler(getAccessDeniedHandler());
// ...
}
项目中还是用了统一异常处理器 DefaultHandlerExceptionResolver:
@Slf4j
@Component
@ControllerAdvice
public class MyGlobalExceptionHandler extends DefaultHandlerExceptionResolver {
@ResponseBody
@ExceptionHandler(Exception.class)
public R defaultErrorHandler(HttpServletRequest request, HttpServletResponse response, Object handler, Exception e) {
e.printStackTrace(); //打印堆栈
log.info("DefaultHandlerExceptionResolver 统一异常处理器...");//打印异常日志
}
}
问题来了,当无权访问时,并不是执行 MyAccessDeniedHandler (尽管它已经在 SpringSecurity 中已经注册异常处理器),这个异常始终会在 MyGlobalExceptionHandler 中处理,打印了堆栈信息和异常日志:
2021-04-21 13:52:30.226 INFO 32684 --- [io-18000-exec-2] c.d.m.s.TokenAuthenticationFilter : TokenAuthenticationFilter...token = d72b29cbaa074cba99377cec73999575
2021-04-21 13:52:30.235 INFO 32684 --- [io-18000-exec-2] c.d.mp.support.MyGlobalExceptionHandler : DefaultHandlerExceptionResolver 统一异常处理器...
2021-04-21 13:52:30.235 INFO 32684 --- [io-18000-exec-2] c.d.mp.support.MyGlobalExceptionHandler : 无权限访问...http://127.0.0.1:18000/test/hello R(code=403, data=null, message=没有权限访问, time=1618984350235)
org.springframework.security.access.AccessDeniedException: 不允许访问
at org.springframework.security.access.vote.AffirmativeBased.decide(AffirmativeBased.java:73)
at org.springframework.security.access.intercept.AbstractSecurityInterceptor.attemptAuthorization(AbstractSecurityInterceptor.java:238)
at org.springframework.security.access.intercept.AbstractSecurityInterceptor.beforeInvocation(AbstractSecurityInterceptor.java:208)
at org.springframework.security.access.intercept.aopalliance.MethodSecurityInterceptor.invoke(MethodSecurityInterceptor.java:58)
at org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:186)
at org.springframework.aop.framework.CglibAopProxy$CglibMethodInvocation.proceed(CglibAopProxy.java:750)
at org.springframework.aop.framework.CglibAopProxy$DynamicAdvisedInterceptor.intercept(CglibAopProxy.java:692)
at com.dkdy.mp.controller.business.TestController$$EnhancerBySpringCGLIB$$fe1a52b5.hello(<generated>)
at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62)
at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
at java.lang.reflect.Method.invoke(Method.java:498)
at org.springframework.web.method.support.InvocableHandlerMethod.doInvoke(InvocableHandlerMethod.java:197)
at org.springframework.web.method.support.InvocableHandlerMethod.invokeForRequest(InvocableHandlerMethod.java:141)
at org.springframework.web.servlet.mvc.method.annotation.ServletInvocableHandlerMethod.invokeAndHandle(ServletInvocableHandlerMethod.java:106)
at org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerAdapter.invokeHandlerMethod(RequestMappingHandlerAdapter.java:894)
at org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerAdapter.handleInternal(RequestMappingHandlerAdapter.java:808)
at org.springframework.web.servlet.mvc.method.AbstractHandlerMethodAdapter.handle(AbstractHandlerMethodAdapter.java:87)
at org.springframework.web.servlet.DispatcherServlet.doDispatch(DispatcherServlet.java:1060)
at org.springframework.web.servlet.DispatcherServlet.doService(DispatcherServlet.java:962)
at org.springframework.web.servlet.FrameworkServlet.processRequest(FrameworkServlet.java:1006)
at org.springframework.web.servlet.FrameworkServlet.doGet(FrameworkServlet.java:898)
at javax.servlet.http.HttpServlet.service(HttpServlet.java:626)
at org.springframework.web.servlet.FrameworkServlet.service(FrameworkServlet.java:883)
at javax.servlet.http.HttpServlet.service(HttpServlet.java:733)
at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:227)
at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:162)
at org.apache.tomcat.websocket.server.WsFilter.doFilter(WsFilter.java:53)
..............
如何让无权访问的异常被 MyAccessDeniedHandler 捕获处理,而不是让统一异常处理器来处理 SpringSecurity 的无权访问异常呢?