Spring:如何使过滤器抛出自定义异常?

6

我创建了一个过滤器,用于验证每个请求头的JWT令牌:

public class JWTAuthenticationFilter extends GenericFilterBean {

    private UserDetailsService customUserDetailsService;
    private static Logger logger = LoggerFactory.getLogger(JWTAuthenticationFilter.class);
    private final static UrlPathHelper urlPathHelper = new UrlPathHelper();

    public JWTAuthenticationFilter(UserDetailsService customUserDetailsService) {
        this.customUserDetailsService = customUserDetailsService;
    }

    @Override
    public void doFilter(ServletRequest request, ServletResponse response, FilterChain filterChain) throws IOException, ServletException {
        Authentication authentication = AuthenticationService.getAuthentication((HttpServletRequest) request, customUserDetailsService);
        SecurityContextHolder.getContext().setAuthentication(authentication);
        if (authentication == null) {
            logger.debug("failed authentication while attempting to access " + urlPathHelper.getPathWithinApplication((HttpServletRequest) request));
        }
        filterChain.doFilter(request, response);
    }

}

我想抛出一个自定义异常,并且该异常返回一个响应:

@ResponseStatus(value=HttpStatus.SOMECODE, reason="There was an issue with the provided authentacion information")  // 409
public class CustomAuthenticationException extends RuntimeException {

    private static final long serialVersionUID = 6699623945573914987L;

}

我该如何做?如何设计最佳方案来捕获过滤器抛出的异常?Spring Security是否提供了任何异常处理机制,我可以使用并在一个点上捕获所有内容?是否有其他方法在过滤器中抛出自定义异常?
注意:这里还有另一个问题,在此处链接 https://stackoverflow.com/questions/40987437/spring-filter-throws-custom-exception。其被接受的答案并没有回答我的问题。我想在到达任何控制器之前返回响应。
我想要处理的错误情况: 1.客户端发送空值的Authorization头。 2.客户端发送格式不正确的令牌。
在这两种情况下,我会收到HTTP状态码为500的响应。我想要得到4XX代码返回。

但应该是“AuthenticationException”来执行失败处理程序。 我在我的问题中添加了一些示例案例。 - Arian
您可以通过添加自定义成功和失败处理程序,并将您的自定义过滤器扩展为AbstractAuthenticationProcessingFilter来实现此功能。 - Afridi
@ArianHosseinzadeh 在你的情况下可以抛出哪些类型的异常? - Afridi
IllegalArgumentException, MalformedJwtException, SignatureException 是一些例子。 - Arian
我认为所有这些异常应该转换为一个通用异常。 - Afridi
显示剩余11条评论
3个回答

4
请看 @ControllerAdvice
这是我的项目的一个例子。
@ControllerAdvice
@RestController
public class GlobalExceptionHandler {

    private final Logger log = Logger.getLogger(this.getClass().getSimpleName());

    @ResponseStatus(HttpStatus.BAD_REQUEST)
    @ExceptionHandler(value = RuntimeException.class)
    public Response handleBaseException(RuntimeException e) {
        log.error("Error", e);
        Error error = new Error(HttpStatus.BAD_REQUEST.value(), HttpStatus.BAD_REQUEST.name());
        return Response.status(HttpStatus.BAD_REQUEST.value()).error(error, null).build();
    }

    @ResponseStatus(HttpStatus.NOT_FOUND)
    @ExceptionHandler(value = NoHandlerFoundException.class)
    public Response handleNoHandlerFoundException(Exception e) {
        log.error("Error", e);
        Error error = new Error(HttpStatus.NOT_FOUND.value(), HttpStatus.NOT_FOUND.name());
        return Response.status(HttpStatus.NOT_FOUND.value()).error(error, null).build();
    }

    @ExceptionHandler(value = AuthenticationCredentialsNotFoundException.class)
    public Response handleException(AuthenticationCredentialsNotFoundException e) {     
        log.error("Error", e);
        Error error = new Error(ErrorCodes.INVALID_CREDENTIALS_CODE, ErrorCodes.INVALID_CREDENTIALS_MSG);
        return Response.status(ErrorCodes.INVALID_CREDENTIALS_CODE).error(error, null).build();
    }

    @ResponseStatus(HttpStatus.UNAUTHORIZED)
    @ExceptionHandler(value = UnauthorisedException.class)
    public Response handleNotAuthorizedExceptionException(UnauthorisedException e) {        
//      log.error("Error", e);
        return Response.unauthorized().build();
    }

    @ExceptionHandler(value = Exception.class)
    public String handleException(Exception e) {
        log.error("Error", e);
        return e.getClass().getName() + " 14" + e.getMessage();
    }


}

编辑:
我相信您可以在 do Filter 方法中使用 response.sendError。
@Override
public void doFilter(ServletRequest request, ServletResponse response, FilterChain filterChain) throws IOException, ServletException {
    Authentication authentication = AuthenticationService.getAuthentication((HttpServletRequest) request, customUserDetailsService);
    SecurityContextHolder.getContext().setAuthentication(authentication);
    if (authentication == null) {
        logger.debug("failed authentication while attempting to access " + urlPathHelper.getPathWithinApplication((HttpServletRequest) request));
        response.sendError(HttpServletResponse.SC_UNAUTHORIZED, "Invalid authentication.");
        setUnauthorizedResponse(response);
        return;
    }
    filterChain.doFilter(request, response);
}

public void setUnauthorizedResponse(HttpServletResponse response) {
    response.setStatus(HttpServletResponse.SC_UNAUTHORIZED);
    response.setContentType("application/json");
    Response unAuthorizedResponse = Response.unauthorized().build();
    try {
        PrintWriter out = response.getWriter();
        out.println(unAuthorizedResponse.toJsonString());
    } catch (IOException e) {
        log.error("Error", e);
    }
}

7
控制器建议(Controller advice)只能处理控制器和服务(services)抛出的异常。 - Afridi
我编辑了我的问题并添加了两个示例案例。例如,当JWT字符串为空时,会出现MalformedJwtExceptionIllegalArgumentException。我无法为IllegalArgumentException设置全局异常处理程序。这并不总是关于身份验证的问题。它可能与其他错误有关。 - Arian
谢谢您的回答,我会将编辑移到顶部,并且在调用Response.unauthorized()时我也遇到了The method unauthorized() is undefined的问题。 - user7294900
响应是自己的类,可以使用您自己的类。 - Drunken Daddy

0

免责声明:这不是对所提问问题的回答,而是对Arian所问问题的后续解决方案。

如上所述,请看一下如何在spring容器还没有为我们提供bean访问权限的情况下进行自动装配。

在这里我正在使用自动装配BlacklistJwtRepo。

if (blacklistJwtRepo == null) { //Lazy Load because filter
        ServletContext servletContext = req.getServletContext();
        WebApplicationContext webApplicationContext =  WebApplicationContextUtils.getWebApplicationContext(servletContext);
        blacklistJwtRepo = webApplicationContext.getBean(BlacklistJwtRepo.class);
    }

这是我获取 req 对象的地方 -

    @Override
    public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws IOException, ServletException {
    HttpServletRequest req = (HttpServletRequest) request;

最终代码如下 -

    @Override
public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain)
        throws IOException, ServletException {
    HttpServletRequest req = (HttpServletRequest) request;      
    System.out.println("blacklistJwtRepo : " + blacklistJwtRepo);
    //till here the autowired repo (blacklistJwtRepo) is null
    if (blacklistJwtRepo == null) { //Lazy Load because filter
        ServletContext servletContext = req.getServletContext();
        WebApplicationContext webApplicationContext = WebApplicationContextUtils.getWebApplicationContext(servletContext);
        blacklistJwtRepo = webApplicationContext.getBean(BlacklistJwtRepo.class);
    }

0

我曾经遇到过JWT令牌的同样问题,并在这个问题上发布了解决方案,因为那里的问题类似(他遇到了过滤器异常的问题)


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接