无法在Spring Boot中使用自定义异常处理程序捕获MalformedJwtException。

huangapple go评论64阅读模式
英文:

Unable to catch MalformedJwtException in Spring Boot with custom exception handler

问题

我正在使用Spring Boot 3.0.3,Java 17和io.jsonwebtoken v0.11.5。我已经实现了Spring Security,并且它运行得很好。我想要定制的一件事是当没有传递jwt令牌或者传递了错误的jwt令牌时,JSON响应是空的,并且状态为403禁止。我想在这条消息中添加一段文本。

因此,我尝试在我的自定义异常处理程序中捕获MalformedJwtException,但它没有被捕获。

我有一个JwtAuthenticationFilter类,从请求头中提取JWT令牌并对其进行验证。如果令牌无效,则会抛出MalformedJwtException。我尝试使用try-catch语句并抛出我的自定义异常,但它也没有被捕获。

这是JwtAuthenticationFilter中相关的代码(doFilterInternal方法):

try {
  jwt = authHeader.substring(authHeaderStartsWith.length());
  userEmail = jwtService.extractUsername(jwt);
} catch (Exception ex) {
  throw new CustomJWTException("要访问此资源,必须提供有效的安全令牌", HttpStatus.UNAUTHORIZED);
}

这是我的自定义异常处理程序:

@ControllerAdvice
public class GlobalExceptionHandler {
  @ExceptionHandler(value = {CustomException.class})
  public ResponseEntity<ResponseWrapper> handleCustomException(CustomException ex) {
    var response = new ResponseWrapper(ex.getHttpStatus(), ex.getMessage());
    return ResponseEntity.status(ex.getHttpStatus()).body(response);
  }

  @ExceptionHandler(value = {CustomJWTException.class})
  public ResponseEntity<ResponseWrapper> handleCustomJWTException(CustomJWTException ex) {
    var message = ex.getMessage();

    if (message == null || message.isEmpty()) {
      message = "要访问此资源,必须提供有效的安全令牌";
    }

    var response = new ResponseWrapper(HttpStatus.UNAUTHORIZED, message);
    return ResponseEntity.status(HttpStatus.UNAUTHORIZED).body(response);
  }

  @ExceptionHandler(value = {MalformedJwtException.class})
  public ResponseEntity<ResponseWrapper> handleMalformedJwtException(MalformedJwtException ex) {
    var message = ex.getMessage();

    if (message == null || message.isEmpty()) {
      message = "要访问此资源,必须提供有效的安全令牌";
    }

    var response = new ResponseWrapper(HttpStatus.UNAUTHORIZED, message);
    return ResponseEntity.status(HttpStatus.UNAUTHORIZED).body(response);
  }
}

我确信doFilterInternal方法中的代码被执行并且运行得很完美。我的CustomJWTException也被抛出(甚至在调试控制台中显示),但由于某种原因它没有进入我的异常处理程序。

我还知道我的异常处理程序是有效的,因为我在其他服务中抛出CustomException,它运行得很好(异常处理程序捕获它)。

我已经尝试过调整@Order(Ordered.HIGHEST_PRECEDENCE),但没有任何变化。

我该如何实现期望的结果?

英文:

I'm using Spring Boot 3.0.3, Java 17 and io.jsonwebtoken v0.11.5. I've implemented Spring Security and it works great. One thing that I would like to customize is a JSON response when there's no jwt token passed or when there's wrong jwt token passed, I'm just getting an empty JSON response with Status 403 forbidden. I'd like to add a text to that message.

So I am trying to catch a MalformedJwtException in my custom exception handler, but it's not being caught.

I have a JwtAuthenticationFilter class which extracts the JWT token from the request header and validates it. If the token is not valid, it throws a MalformedJwtException. I tried using try-catch statement and throwing my custom exception but it doesn't get caught either.

Here is the relevant code from JwtAuthenticationFilter (method doFilterInternal):

try {
  jwt = authHeader.substring(authHeaderStartsWith.length());
  userEmail = jwtService.extractUsername(jwt);
} catch (Exception ex) {
  throw new CustomJWTException(&quot;To access this resource, you must provide a valid security token&quot;, HttpStatus.UNAUTHORIZED);
}

And here is my custom exception handler:

@ControllerAdvice
public class GlobalExceptionHandler {
  @ExceptionHandler(value = {CustomException.class})
  public ResponseEntity&lt;ResponseWrapper&gt; handleCustomException(CustomException ex) {
    var response = new ResponseWrapper(ex.getHttpStatus(), ex.getMessage());
   return ResponseEntity.status(ex.getHttpStatus()).body(response);
  }

  @ExceptionHandler(value = {CustomJWTException.class})
  public ResponseEntity&lt;ResponseWrapper&gt; handleCustomJWTException(CustomJWTException ex) {
    var message = ex.getMessage();

    if (message == null || message.isEmpty()) {
      message = &quot;To access this resource, you must provide a valid security token&quot;;
    }

    var response = new ResponseWrapper(HttpStatus.UNAUTHORIZED, message);
    return ResponseEntity.status(HttpStatus.UNAUTHORIZED).body(response);
  }

  @ExceptionHandler(value = {MalformedJwtException.class})
  public ResponseEntity&lt;ResponseWrapper&gt; handleMalformedJwtException(MalformedJwtException ex) {
    var message = ex.getMessage();

    if (message == null || message.isEmpty()) {
      message = &quot;To access this resource, you must provide a valid security token&quot;;
    }

    var response = new ResponseWrapper(HttpStatus.UNAUTHORIZED, message);
    return ResponseEntity.status(HttpStatus.UNAUTHORIZED).body(response);
  }
}

I know for sure that the code in the doFilterInternal method gets executed and works perfectly. Also my CustomJWTException is also getting thrown (even shown in the Debug console) but for some reason it doesn't end up in my exception handler.\

I also know for a fact that my exception handler works because I'm throwing CustomException is some other service and it works great (the exceptioin handler catches it).

I've tried tweaking @Order(Ordered.HIGHEST_PRECEDENCE) but nothing changed.

How can I achieve the desired result?

答案1

得分: 3

基本上是因为Spring Security和Spring MVC有不同的异常处理机制。

Spring Security在请求真正到达并由Spring MVC Servlet(即DispatcherServlet)处理之前插入了许多过滤器来验证HTTP请求(详见此链接)。

@ExceptionHandler是Spring MVC的异常处理机制,只有在Spring MVC处理请求期间发生异常时才会起作用。

现在您的请求在实际被Spring MVC处理之前就失败并返回到JwtAuthenticationFilter,这就是为什么@ExceptionHandler不起作用的原因。

如何修复它取决于您的JwtAuthenticationFilter实现。通常,我会委托给一个AuthenticationEntryPoint来处理认证失败的异常。在AuthenticationEntryPoint中,使用Jackson来准备错误响应的JSON并返回。

英文:

Basically it is because spring-security and spring-mvc has different mechanism to handle the exception.

Spring Security inserts many Filters to validate the HTTP request before the request really hit and processed by the spring-mvc Servlet (i.e DispatcherServlet) (see this for details)

And @ExceptionHandler is exception handling mechanism of spring-mvc and so it will only work if exception occurs during spring-mvc process the request.

Now your request already fail and return in the JwtAuthenticationFilter before it has chance to be processed by spring-mvc and that 's why @ExceptionHandler does not work.

How to fix it depends on your JwtAuthenticationFilter implementation . I usually delegate to an AuthenticationEntryPoint to process the exception if the authentication fail. In AuthenticationEntryPoint, use Jackson to prepare the error response JSON and return.

huangapple
  • 本文由 发表于 2023年3月3日 21:03:09
  • 转载请务必保留本文链接:https://go.coder-hub.com/75627422.html
匿名

发表评论

匿名网友

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen:

确定