说明
SpringSecurity的异常过滤器ExceptionTranslationFilter
默认在倒数第二位出现,即过滤器即将走完的时候出现,不知道SpringSecurity为啥这样实现,但是这样处理的话就会导致前面过滤器中的异常无法处理到。
最终的结果就是对于非认证异常等就会走默认的实现,即重定向到/error页面,但前后端分离项目中我们又没有/error页面,因此又会出现一个循环异常,导致报错到前端的异常为InsufficientAuthenticationException
,这个异常是很隐晦的,基本看不出来本质的异常信息,毕竟已经重定向了N次了。
因此前后端分离项目中,我们需要第一次报错出去就返回异常信息。
实现
方案一: SpringSecurity中加入过滤器链
我们需要在Config配置中添加过滤器,在WebAsyncManagerIntegrationFilter
之前添加
1 2 3 4
| protected void configure(HttpSecurity http) throws Exception { http.addFilterBefore(new ExceptionTranslationFilter(exceptionResolverHandler), WebAsyncManagerIntegrationFilter.class) }
|
未添加之前的过滤器链为:
1 2 3 4 5 6 7 8 9 10 11 12 13
| 1 = {WebAsyncManagerlntegrationFilter@9302} 2 = {SecurityContextPersistenceFilter@9303} 3 = {HeaderWriterFilter@9288} 4= {LogoutFilter@9304} 5 = {JsonLoginFilter@9305} 6 = {AuthUserContextFilter@9306} 7 = {ConcurrentSessionFilter@9307} 8 = {RequestCacheAwareFilter@9308} 9 = {SecurityContextHolderAwareRequestFilter@9309} 10 = {AnonymousAuthenticationFilter@9310} 11 = {SessionManagementFilter@9311} 12 = {ExceptionTranslationFilter@9312} 13 = {FilterSecuritylnterceptor@9313}
|
添加之后的过滤器链为:
1 2 3 4 5 6 7 8 9 10 11 12 13 14
| 1 = {ExceptionTranslationFilter@9304} 2 = {ExceptionTranslationFilter@9306} 3 = {WebAsyncManagerlntegrationFilter@9307} 4 = {SecurityContextPersistenceFilter@9308} 5 = {HeaderWriterFilter@9291} 6 = {LogoutFilter@9309} 7 = {JsonLoginFilter@9310} 8 = {AuthUserContextFilter@9311} 9 = {ConcurrentSessionFilter@9312} 10 = {RequestCacheAwareFilter@9313} 11 = {SecurityContextHolderAwareRequestFilter@9314) 12 = {AnonymousAuthenticationFilter@9315} 13 = {SessionManagementFilter@9316) 14 = {FilterSecuritylnterceptor@9317}
|
这里有一个问题,就是可以看见最后一个异常过滤器前移了,移动到了WebAsyncManagerlntegrationFilter
之前,但是为啥会移动呢,猜测大概在配置的时候addFilterBefore
添加ExceptionTranslationFilter
时就会调整ExceptionTranslationFilter
的位置,就出现了两个
因此,建议咱们还是自己写一个过滤器处理
方案二: 在SpirngSecurity过滤器前添加过滤器链
SpringSecurity的过滤器链其实也是基于Filter实现的,在它自己的filter中维护了一系列的security过滤器逐一调用,最后调用实际的方法
那么我们实现的时候可以在其之前添加一个保底的过滤器,即当SpringSecurity没有完成处理任何一个异常的时候就返回此过滤器
此处需要注意什么Security什么时候重定向到错误页面的,但是测试发现并没有重定向,所以先就这样
经测试SpringSecurity的过滤器的优先级为-100
因此我们配置的优先级要小于100,我们这里直接配置-101
即可
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48
|
@Order(-101) public class BeforeSecurityFilter extends OncePerRequestFilter {
HandlerExceptionResolver handlerExceptionResolver;
public BeforeSecurityFilter(HandlerExceptionResolver handlerExceptionResolver) { this.handlerExceptionResolver = handlerExceptionResolver; }
@Override protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain filterChain) throws ServletException, IOException { String requestURI = request.getRequestURI(); if (requestURI.startsWith("/error")) { resolver(request,response,new RuntimeException("没有此页面 /error")); } try { filterChain.doFilter(request, response); } catch (ServletException e) { throw new RuntimeException(e); } catch (Exception ex){ resolver(request,response,ex); } finally { logger.warn("security前处理异常"); } } private void resolver(HttpServletRequest request, HttpServletResponse response, Exception ex){ ModelAndView modelAndView = handlerExceptionResolver.resolveException(request, response, null, ex); if (modelAndView == null) { logger.warn("返回无处理解析器,按照默认返回"); response.setContentType("application/json;charset=utf-8"); try { PrintWriter out = response.getWriter(); out.write("返回异常,且没有配置返回解析,异常信息:" + ex.getMessage()); out.flush(); out.close(); } catch (IOException e) { throw new AuthenticationServiceException("BeforeSecurityFilter 解析返回异常", e); } } } }
|
然后在配置中配置Bean即可
1 2 3 4
| @Bean BeforeSecurityFilter beforeSecurityFilter(HandlerExceptionResolver handlerExceptionResolver){ return new BeforeSecurityFilter(handlerExceptionResolver); }
|
这样就直接保底执行异常