捕获从AuthenticationEntryPoint

时间:2019-06-04 18:00:29

标签: java spring-boot spring-security servlet-filters

我有一个JWTAuthFilter,它在验证令牌的地方扩展了OncePerRequestFilter

validateToken方法将引发自定义异常(扩展org.springframework.security.core.AuthenticationException的CredentialsChangedException和TooManyDevicesException)

这些异常已正确捕获在过滤器中,但是当它们前进到AuthenticationEntryPoint时,AuthenticationException变成instanceof InsufficientAuthenticationException和我想返回的自定义错误消息响应丢失了。

@Component
public class JwtAuthFilter extends OncePerRequestFilter {

    private static final String BEARER = "Bearer ";

    private static final Logger logger = LoggerFactory.getLogger(JwtAuthFilter.class);

    @Autowired
    private UserDetailsService jwtUserDetailsService;

    @Autowired
    private JwtTokenUtil jwtTokenUtil;

    @Value ("${jwt.http.request.header}")
    private String tokenHeader;

    @Override
    protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain chain)
                    throws ServletException, IOException {

        final String requestTokenHeader = request.getHeader(this.tokenHeader);

        String username = null;
        String jwtToken = null;

        if((requestTokenHeader != null) && requestTokenHeader.startsWith(BEARER)) {
            jwtToken = requestTokenHeader.substring(7);
            try {
                username = jwtTokenUtil.getUsernameFromToken(jwtToken);
                if((username != null) && (SecurityContextHolder.getContext()
                                                               .getAuthentication() == null)) {

                    UserDetails userDetails = this.jwtUserDetailsService.loadUserByUsername(username);


                    if(jwtTokenUtil.validateToken(jwtToken, userDetails)) {
                        UsernamePasswordAuthenticationToken usernamePasswordAuthenticationToken =
                                        new UsernamePasswordAuthenticationToken(userDetails, null,
                                                                                userDetails.getAuthorities());
                        usernamePasswordAuthenticationToken.setDetails(new WebAuthenticationDetailsSource().buildDetails(request));
                        SecurityContextHolder.getContext()
                                             .setAuthentication(usernamePasswordAuthenticationToken);
                    }
                }
            }
            catch (IllegalArgumentException e) {
                logger.error("Unable to get username from JWT. ", e.getLocalizedMessage());
            }
            catch (ExpiredJwtException e) {
                logger.warn("Expired JWT. ", e.getLocalizedMessage());
            }
            catch (Exception e) {
                logger.error(e.getLocalizedMessage());
            }
        }

        chain.doFilter(request, response);
    }
}
@Component
public class JwtUnAuthorizedResponseAuthenticationEntryPoint implements AuthenticationEntryPoint , Serializable {

    private static final long serialVersionUID = - 8970718410437077606L;

    @Override
    public void commence(HttpServletRequest request, HttpServletResponse response, AuthenticationException e)
                    throws IOException {

        if(e instanceof TooManyDevicesException) {
            response.sendError(HttpServletResponse.SC_UNAUTHORIZED, Constants.TOO_MANY_DEVICES);
        }

        else if(e instanceof CredentialsChangedException) {
            response.sendError(HttpServletResponse.SC_UNAUTHORIZED, Constants.CREDENTIALS_CHANGED);
        }
        else {
            response.sendError(HttpServletResponse.SC_UNAUTHORIZED, Constants.JWT_EXPIRED);
        }

    }
}

我想从我的过滤器发送适当的未经授权的回复,有没有办法做到这一点?

2 个答案:

答案 0 :(得分:0)

我认为,一旦您在AuthenticationException中抓到JwtAuthFilter,就不应继续使用下一个过滤器,因为很可能AnonymousAuthenticationFilter将位于过滤器链的后面,如果SecurityContextHolder为空(即,验证失败时发生),此过滤器会将当前请求配置为匿名用户。 InsufficientAuthenticationException很可能是由于Spring认为当前请求是访问某些受保护的URL或方法的匿名用户。

相反,一旦您在AuthenticationException中抓到JwtAuthFilter,就应该调用AuthenticationEntryPoint.commence()并结束过滤链。 https://repl.it/@Evandronao/WellmadeMetallicFlashdrives也是现在这样。

所以,我建议将JwtAuthFilter修改为:

@Component
public class JwtAuthFilter extends OncePerRequestFilter {

  @Autowired
  private AuthenticationEntryPoint authenticationEntryPoint;


  @Override
  protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain chain)
                    throws ServletException, IOException {
         try{

              //validate JWT

          } catch (AuthenticationException e) {
               logger.error(e.getLocalizedMessage());
                SecurityContextHolder.clearContext();
                this.authenticationEntryPoint.commence(request, response, e);
                return;
          }
    }
}

答案 1 :(得分:0)

目前,作为解决方法,我只是将JwtAuthFilter中的自定义错误添加为request属性,并在AuthenticationEntryPoint中检索了它