在身份验证失败时抛出异常

时间:2016-05-16 14:43:55

标签: spring-security exception-handling spring-boot

我在spring-boot应用程序中实现了一些基于令牌的身份验证。我有一个过滤器,在该过滤器中,我正在执行以下操作:

@Override
public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws IOException, ServletException {
    HttpServletRequest httpRequest = (HttpServletRequest) request;
    String authToken = httpRequest.getHeader("X-TOKEN-AUTH");
    String username = null;

    if (securityEnabled) {
       if (authToken != null) {

            try {
                username = userTokenService.validateToken(authToken);
                UserDetails userDetails = userDetailsService.loadUserByUsername(username);
                UsernamePasswordAuthenticationToken auth = new UsernamePasswordAuthenticationToken(userDetails.getUsername(), null, userDetails.getAuthorities());
                auth.setDetails(new WebAuthenticationDetailsSource().buildDetails(httpRequest));
                SecurityContextHolder.getContext().setAuthentication(auth);

            } catch (AuthenticationException ae) {
                //TODO log something about signature exception
                log.warn(ae.getMessage());
            }
        }

    }

    chain.doFilter(request, response);
}

我还有一个自定义的AuthFailureHandler:

@Component
public class AuthFailureHandler extends SimpleUrlAuthenticationFailureHandler {
    @Override
    public void onAuthenticationFailure(HttpServletRequest request, HttpServletResponse response,
                                        AuthenticationException exception) throws IOException, ServletException {
        response.setStatus(HttpServletResponse.SC_UNAUTHORIZED);

        PrintWriter writer = response.getWriter();
        writer.write(exception.getMessage());
        writer.flush();
    }
}

由于各种原因,我的代码username = userTokenService.validateToken(authToken);会抛出AuthenticationExceptionAuthenticationException是一个扩展Exception的自定义异常。当我捕获此异常时,我仍然希望返回401,但我希望我的消息显示在当前作为JSON发送的Spring Security默认情况下:

{
    "timestamp": 1463408604943,
    "status": 401,
    "error": "Unauthorized",
    "message": "An Authentication object was not found in the SecurityContext",
    "path": "/api/brands/2"
}

我想要,例如......

{
    "timestamp": 1463408604943,
    "status": 401,
    "error": "Unauthorized",
    "message": "Invalid Token: Expired",
    "path": "/api/brands/2"
}

我不确定如何覆盖此行为。

1 个答案:

答案 0 :(得分:0)

所以......我终于想通了。问题是过滤器在食物链上的位置越来越高,所以他们实际上并没有那么多涉及Spring。我在Filter中抛出异常的地方,Spring并不一定能抓住它。过滤器只会抛出500并显示异常消息。要修复它,我只需要在过滤器中捕获我的异常,然后使用适当的http状态调用sendError

try {
  username = userTokenService.validateToken(authToken);
  UserDetails userDetails = userDetailsService.loadUserByUsername(username);
  UsernamePasswordAuthenticationToken auth = new UsernamePasswordAuthenticationToken(userDetails.getUsername(), null, userDetails.getAuthorities());
  auth.setDetails(new WebAuthenticationDetailsSource().buildDetails(httpRequest));
  SecurityContextHolder.getContext().setAuthentication(auth);
  chain.doFilter(request, response);
  return;
} catch (Exception ex) {
  httpResponse.sendError(HttpServletResponse.SC_UNAUTHORIZED, ex.getMessage());
  return;
}

return中的catch语句是为了不调用chain.doFilter(request, response);方法末尾的doFilter

相关问题