简体   繁体   English

如何管理 Spring 中过滤器抛出的异常?

[英]How to manage exceptions thrown in filters in Spring?

I want to use generic way to manage 5xx error codes, let's say specifically the case when the db is down across my whole spring application.我想使用通用方法来管理 5xx 错误代码,让我们具体说一下当数据库在我的整个 spring 应用程序中关闭时的情况。 I want a pretty error json instead of a stack trace.我想要一个漂亮的错误 json 而不是堆栈跟踪。

For the controllers I have a @ControllerAdvice class for the different exceptions and this is also catching the case that the db is stopping in the middle of the request.对于控制器,我有一个@ControllerAdvice class 用于不同的异常,这也捕获了数据库在请求中间停止的情况。 But this is not all.但这并不是全部。 I also happen to have a custom CorsFilter extending OncePerRequestFilter and there when i call doFilter i get the CannotGetJdbcConnectionException and it will not be managed by the @ControllerAdvice .我也碰巧有一个自定义的CorsFilter扩展OncePerRequestFilter并且当我调用doFilter时我得到CannotGetJdbcConnectionException并且它不会由@ControllerAdvice管理。 I read several things online that only made me more confused.我在网上读了几件事,这只会让我更加困惑。

So I have a lot of questions:所以我有很多问题:

  • Do i need to implement a custom filter?我需要实现自定义过滤器吗? I found the ExceptionTranslationFilter but this only handles AuthenticationException or AccessDeniedException .我找到了ExceptionTranslationFilter但这只处理AuthenticationExceptionAccessDeniedException
  • I thought of implementing my own HandlerExceptionResolver , but this made me doubt, I don't have any custom exception to manage, there must be a more obvious way than this.我想实现我自己的HandlerExceptionResolver ,但这让我怀疑,我没有任何自定义异常要管理,一定有比这更明显的方法。 I also tried to add a try/catch and call an implementation of the HandlerExceptionResolver (should be good enough, my exception is nothing special) but this is not returning anything in the response, i get a status 200 and an empty body.我还尝试添加一个 try/catch 并调用HandlerExceptionResolver的实现(应该足够好,我的异常没什么特别的)但这没有在响应中返回任何内容,我得到一个状态 200 和一个空体。

Is there any good way to deal with this?有什么好的方法可以解决这个问题吗? Thanks谢谢

So this is what I did: 所以这就是我做的:

I read the basics about filters here and I figured out that I need to create a custom filter that will be first in the filter chain and will have a try catch to catch all runtime exceptions that might occur there. 我在这里阅读了有关过滤器的基础知识,并且我发现我需要创建一个自定义过滤器,该过滤器将首先位于过滤器链中,并且将有一个try catch来捕获可能在那里发生的所有运行时异常。 Then i need to create the json manually and put it in the response. 然后我需要手动创建json并将其放入响应中。

So here is my custom filter: 所以这是我的自定义过滤器:

public class ExceptionHandlerFilter extends OncePerRequestFilter {

    @Override
    public void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain filterChain) throws ServletException, IOException {
        try {
            filterChain.doFilter(request, response);
        } catch (RuntimeException e) {

            // custom error response class used across my project
            ErrorResponse errorResponse = new ErrorResponse(e);

            response.setStatus(HttpStatus.INTERNAL_SERVER_ERROR.value());
            response.getWriter().write(convertObjectToJson(errorResponse));
    }
}

    public String convertObjectToJson(Object object) throws JsonProcessingException {
        if (object == null) {
            return null;
        }
        ObjectMapper mapper = new ObjectMapper();
        return mapper.writeValueAsString(object);
    }
}

And then i added it in the web.xml before the CorsFilter . 然后我在CorsFilter之前的web.xml中添加它。 And it works! 它的工作原理!

<filter> 
    <filter-name>exceptionHandlerFilter</filter-name> 
    <filter-class>xx.xxxxxx.xxxxx.api.controllers.filters.ExceptionHandlerFilter</filter-class> 
</filter> 


<filter-mapping> 
    <filter-name>exceptionHandlerFilter</filter-name> 
    <url-pattern>/*</url-pattern> 
</filter-mapping> 

<filter> 
    <filter-name>CorsFilter</filter-name> 
    <filter-class>org.springframework.web.filter.DelegatingFilterProxy</filter-class> 
</filter> 

<filter-mapping>
    <filter-name>CorsFilter</filter-name>
    <url-pattern>/*</url-pattern>
</filter-mapping>

I come across this issue myself and I performed the steps below to reuse my ExceptionController that is annotated with @ControllerAdvise for Exceptions thrown in a registered Filter. 我自己遇到了这个问题,并且我执行了以下步骤来重用我的ExceptionController ,该注释使用@ControllerAdvise注释,用于在已注册的Filter中抛出的Exceptions

There are obviously many ways to handle exception but, in my case, I wanted the exception to be handled by my ExceptionController because I am stubborn and also because I don't want to copy/paste the same code (ie I have some processing/logging code in ExceptionController ). 显然有很多方法可以处理异常,但在我的情况下,我希望异常由我的ExceptionController处理,因为我很顽固,也因为我不想复制/粘贴相同的代码(即我有一些处理/在ExceptionController记录代码)。 I would like to return the beautiful JSON response just like the rest of the exceptions thrown not from a Filter. 我想返回漂亮的JSON响应,就像从Filter中抛出的其他异常一样。

{
  "status": 400,
  "message": "some exception thrown when executing the request"
}

Anyway, I managed to make use of my ExceptionHandler and I had to do a little bit of extra as shown below in steps: 无论如何,我设法使用了我的ExceptionHandler ,我不得不做一些额外的操作,如下所示:

Steps 脚步


  1. You have a custom filter that may or may not throw an exception 您有一个自定义过滤器,可能会或可能不会抛出异常
  2. You have a Spring controller that handles exceptions using @ControllerAdvise ie MyExceptionController 你有一个Spring控制器,使用@ControllerAdvise即MyExceptionController来处理异常

Sample code 示例代码

//sample Filter, to be added in web.xml
public MyFilterThatThrowException implements Filter {
   //Spring Controller annotated with @ControllerAdvise which has handlers
   //for exceptions
   private MyExceptionController myExceptionController; 

   @Override
   public void destroy() {
        // TODO Auto-generated method stub
   }

   @Override
   public void init(FilterConfig arg0) throws ServletException {
       //Manually get an instance of MyExceptionController
       ApplicationContext ctx = WebApplicationContextUtils
                  .getRequiredWebApplicationContext(arg0.getServletContext());

       //MyExceptionHanlder is now accessible because I loaded it manually
       this.myExceptionController = ctx.getBean(MyExceptionController.class); 
   }

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

        try {
           //code that throws exception
        } catch(Exception ex) {
          //MyObject is whatever the output of the below method
          MyObject errorDTO = myExceptionController.handleMyException(req, ex); 

          //set the response object
          res.setStatus(errorDTO .getStatus());
          res.setContentType("application/json");

          //pass down the actual obj that exception handler normally send
          ObjectMapper mapper = new ObjectMapper();
          PrintWriter out = res.getWriter(); 
          out.print(mapper.writeValueAsString(errorDTO ));
          out.flush();

          return; 
        }

        //proceed normally otherwise
        chain.doFilter(request, response); 
     }
}

And now the sample Spring Controller that handles Exception in normal cases (ie exceptions that are not usually thrown in Filter level, the one we want to use for exceptions thrown in a Filter) 现在是在正常情况下处理Exception的示例Spring Controller(例如,通常不会在Filter级别中引发的异常,我们要用于在Filter中抛出异常的异常)

//sample SpringController 
@ControllerAdvice
public class ExceptionController extends ResponseEntityExceptionHandler {

    //sample handler
    @ResponseStatus(value = HttpStatus.BAD_REQUEST)
    @ExceptionHandler(SQLException.class)
    public @ResponseBody MyObject handleSQLException(HttpServletRequest request,
            Exception ex){
        ErrorDTO response = new ErrorDTO (400, "some exception thrown when "
                + "executing the request."); 
        return response;
    }
    //other handlers
}

Sharing the solution with those who wish to use ExceptionController for Exceptions thrown in a Filter. 与那些希望在Filter中使用ExceptionController for Exceptions人共享解决方案。

If you want a generic way, you can define an error page in web.xml: 如果需要通用方法,可以在web.xml中定义错误页面:

<error-page>
  <exception-type>java.lang.Throwable</exception-type>
  <location>/500</location>
</error-page>

And add mapping in Spring MVC: 并在Spring MVC中添加映射:

@Controller
public class ErrorController {

    @RequestMapping(value="/500")
    public @ResponseBody String handleException(HttpServletRequest req) {
        // you can get the exception thrown
        Throwable t = (Throwable)req.getAttribute("javax.servlet.error.exception");

        // customize response to what you want
        return "Internal server error.";
    }
}

So, here's what I did based on an amalgamation of the above answers... We already had a GlobalExceptionHandler annotated with @ControllerAdvice and I also wanted to find a way to re-use that code to handle exceptions that come from filters. 所以,这就是我基于上述答案的合并而做的...我们已经使用@ControllerAdvice注释了GlobalExceptionHandler ,我还想找到一种方法来重用该代码来处理来自过滤器的异常。

The simplest solution I could find was to leave the exception handler alone, and implement an error controller as follows: 我能找到的最简单的解决方案是单独留下异常处理程序,并按如下方式实现错误控制器:

@Controller
public class ErrorControllerImpl implements ErrorController {
  @RequestMapping("/error")
  public void handleError(HttpServletRequest request) throws Throwable {
    if (request.getAttribute("javax.servlet.error.exception") != null) {
      throw (Throwable) request.getAttribute("javax.servlet.error.exception");
    }
  }
}

So, any errors caused by exceptions first pass through the ErrorController and are re-directed off to the exception handler by rethrowing them from within a @Controller context, whereas any other errors (not caused directly by an exception) pass through the ErrorController without modification. 因此,由异常引起的任何错误首先通过ErrorController并通过在@Controller上下文中重新引导它们而被重定向到异常处理程序,而任何其他错误(不是由异常直接引起)都会通过ErrorController而不进行修改。

Any reasons why this is actually a bad idea? 为什么这实际上是一个坏主意的任何原因?

This is my solution by overriding default Spring Boot /error handler 这是我的解决方案,通过覆盖默认的Spring Boot /错误处理程序

package com.mypackage;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.autoconfigure.web.ErrorAttributes;
import org.springframework.core.annotation.AnnotationUtils;
import org.springframework.http.HttpStatus;
import org.springframework.http.ResponseEntity;
import org.springframework.util.Assert;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.ResponseStatus;
import org.springframework.web.bind.annotation.RestController;
import org.springframework.web.context.request.RequestAttributes;
import org.springframework.web.context.request.ServletRequestAttributes;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.util.Map;

/**
 * This controller is vital in order to handle exceptions thrown in Filters.
 */
@RestController
@RequestMapping("/error")
public class ErrorController implements org.springframework.boot.autoconfigure.web.ErrorController {

    private final static Logger LOGGER = LoggerFactory.getLogger(ErrorController.class);

    private final ErrorAttributes errorAttributes;

    @Autowired
    public ErrorController(ErrorAttributes errorAttributes) {
        Assert.notNull(errorAttributes, "ErrorAttributes must not be null");
        this.errorAttributes = errorAttributes;
    }

    @Override
    public String getErrorPath() {
        return "/error";
    }

    @RequestMapping
    public ResponseEntity<Map<String, Object>> error(HttpServletRequest aRequest, HttpServletResponse response) {
        RequestAttributes requestAttributes = new ServletRequestAttributes(aRequest);
        Map<String, Object> result =     this.errorAttributes.getErrorAttributes(requestAttributes, false);

        Throwable error = this.errorAttributes.getError(requestAttributes);

        ResponseStatus annotation =     AnnotationUtils.getAnnotation(error.getClass(), ResponseStatus.class);
        HttpStatus statusCode = annotation != null ? annotation.value() : HttpStatus.INTERNAL_SERVER_ERROR;

        result.put("status", statusCode.value());
        result.put("error", statusCode.getReasonPhrase());

        LOGGER.error(result.toString());
        return new ResponseEntity<>(result, statusCode) ;
    }

}

When you want to test a state of application and in case of a problem return HTTP error I would suggest a filter. 当你想测试一个应用程序的状态,如果出现问题,返回HTTP错误,我会建议一个过滤器。 The filter below handles all HTTP requests. 下面的过滤器处理所有HTTP请求。 The shortest solution in Spring Boot with a javax filter. Spring Boot中使用javax过滤器的最短解决方案。

In the implementation can be various conditions. 在实施中可以有各种条件。 In my case the applicationManager testing if the application is ready. 在我的例子中,applicationManager测试应用程序是否准备就绪。

import ...ApplicationManager;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;

import javax.servlet.*;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;

@Component
public class SystemIsReadyFilter implements Filter {

    @Autowired
    private ApplicationManager applicationManager;

    @Override
    public void init(FilterConfig filterConfig) throws ServletException {}

    @Override
    public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws IOException, ServletException {
        if (!applicationManager.isApplicationReady()) {
            ((HttpServletResponse) response).sendError(HttpServletResponse.SC_SERVICE_UNAVAILABLE, "The service is booting.");
        } else {
            chain.doFilter(request, response);
        }
    }

    @Override
    public void destroy() {}
}

I wanted to provide a solution based on the answer of @kopelitsa . 我想根据@kopelitsa的答案提供解决方案。 The main differences being: 主要区别是:

  1. Reusing the controller exception handling by using the HandlerExceptionResolver . 使用HandlerExceptionResolver重用控制器异常处理。
  2. Using Java config over XML config 在XML配置上使用Java配置

First, you need to make sure, that you have a class that handles exceptions occurring in a regular RestController/Controller (a class annotated with @RestControllerAdvice or @ControllerAdvice and method(s) annotated with @ExceptionHandler ). 首先,您需要确保您有一个类来处理常规RestController / Controller(使用@RestControllerAdvice@ControllerAdvice注释的类以及使用@RestControllerAdvice注释的方法)中发生的@ExceptionHandler This handles your exceptions occurring in a controller. 这可以处理控制器中发生的异常。 Here is an example using the RestControllerAdvice: 以下是使用RestControllerAdvice的示例:

@RestControllerAdvice
public class ExceptionTranslator {

    @ExceptionHandler(RuntimeException.class)
    @ResponseStatus(HttpStatus.INTERNAL_SERVER_ERROR)
    public ErrorDTO processRuntimeException(RuntimeException e) {
        return createErrorDTO(HttpStatus.INTERNAL_SERVER_ERROR, "An internal server error occurred.", e);
    }

    private ErrorDTO createErrorDTO(HttpStatus status, String message, Exception e) {
        (...)
    }
}

To reuse this behavior in the Spring Security filter chain, you need to define a Filter and hook it into your security configuration. 要在Spring Security过滤器链中重用此行为,您需要定义过滤器并将其挂钩到安全配置中。 The filter needs to redirect the exception to the above defined exception handling. 过滤器需要将异常重定向到上面定义的异常处理。 Here is an example: 这是一个例子:

@Component
public class FilterChainExceptionHandler extends OncePerRequestFilter {

    private final Logger log = LoggerFactory.getLogger(getClass());

    @Autowired
    @Qualifier("handlerExceptionResolver")
    private HandlerExceptionResolver resolver;

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

        try {
            filterChain.doFilter(request, response);
        } catch (RuntimeException e) {
            log.error("Spring Security Filter Chain RuntimeException:", e);
            resolver.resolveException(request, response, null, e);
        }
    }
}

The created filter then needs to be added to the SecurityConfiguration. 然后,需要将创建的过滤器添加到SecurityConfiguration。 You need to hook it into the chain very early, because all preceding filter's exceptions won't be caught. 您需要很早将它挂钩到链中,因为不会捕获所有前面的过滤器异常。 In my case, it was reasonable to add it before the LogoutFilter . 就我而言,在LogoutFilter之前添加它是合理的。 See the default filter chain and its order in the official docs . 请参阅官方文档中的默认过滤器链及其顺序。 Here is an example: 这是一个例子:

@Configuration
@EnableWebSecurity
public class SecurityConfiguration extends WebSecurityConfigurerAdapter {

    @Autowired
    private FilterChainExceptionHandler filterChainExceptionHandler;

    @Override
    protected void configure(HttpSecurity http) throws Exception {
        http
            .addFilterBefore(filterChainExceptionHandler, LogoutFilter.class)
            (...)
    }

}

Just to complement the other fine answers provided, as I too recently wanted a single error/exception handling component in a simple SpringBoot app containing filters that may throw exceptions, with other exceptions potentially thrown from controller methods. 只是为了补充所提供的其他精细答案,因为我最近想要一个简单的SpringBoot应用程序中的单个错误/异常处理组件,其中包含可能抛出异常的过滤器,以及可能从控制器方法抛出的其他异常。

Fortunately, it seems there is nothing to prevent you from combining your controller advice with an override of Spring's default error handler to provide consistent response payloads, allow you to share logic, inspect exceptions from filters, trap specific service-thrown exceptions, etc. 幸运的是,似乎没有什么可以阻止您将控制器建议与Spring的默认错误处理程序相结合,以提供一致的响应有效负载,允许您共享逻辑,检查过滤器中的异常,捕获特定的服务抛出异常等。

Eg 例如


@ControllerAdvice
@RestController
public class GlobalErrorHandler implements ErrorController {

  @ResponseStatus(HttpStatus.BAD_REQUEST)
  @ExceptionHandler(ValidationException.class)
  public Error handleValidationException(
      final ValidationException validationException) {
    return new Error("400", "Incorrect params"); // whatever
  }

  @ResponseStatus(HttpStatus.INTERNAL_SERVER_ERROR)
  @ExceptionHandler(Exception.class)
  public Error handleUnknownException(final Exception exception) {
    return new Error("500", "Unexpected error processing request");
  }

  @RequestMapping("/error")
  public ResponseEntity handleError(final HttpServletRequest request,
      final HttpServletResponse response) {

    Object exception = request.getAttribute("javax.servlet.error.exception");

    // TODO: Logic to inspect exception thrown from Filters...
    return ResponseEntity.badRequest().body(new Error(/* whatever */));
  }

  @Override
  public String getErrorPath() {
    return "/error";
  }

}

You can use the following method inside the catch block:您可以在 catch 块中使用以下方法:

response.sendError(HttpStatus.UNAUTHORIZED.value(), "Invalid token")

Notice that you can use any HttpStatus code and a custom message.请注意,您可以使用任何 HttpStatus 代码和自定义消息。

I had the same issue in webflux, going on the theme that someone is looking to resuse there @ControllerAdvice, you do not want to throw a direct exception or return a mono error in the webfilter, however you want to set the response to be the mono error.我在 webflux 中遇到了同样的问题,主题是有人希望在那里重新使用@ControllerAdvice,你不想抛出直接异常或在 webfilter 中返回 mono 错误,但是你想将响应设置为mono 错误。

    public class YourFilter implements WebFilter {


    @Override
    public Mono<Void> filter(final ServerWebExchange exchange, final WebFilterChain chain) {
        exchange.getResponse().writeWith(Mono.error(new YouException()));
        return chain.filter(exchange)
    }
}

In Filters, we don't have a control with @ControllerAdvice or @RestControllerAdvice to handle our exceptions that could occur at the time of doing the authentication.在过滤器中,我们没有带有@ControllerAdvice@RestControllerAdvice的控件来处理我们在进行身份验证时可能发生的异常。 Because, DispatcherServlet will only come into picture after the Controller class hits.因为,DispatcherServlet 只会在 Controller class 命中后出现。 So, we need to do the following.所以,我们需要做到以下几点。

  1. we need to have我们需要有

    HttpServletResponse httpResponse = (HttpServletResponse) response; HttpServletResponse httpResponse = (HttpServletResponse) 响应;

"response" object we can pass it from public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) of GenericFilterBean.java implementation class. 2) We can use the below utility class to write or print our error JSON model or String object into the ServletResponse output stream. "response" object we can pass it from public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) of GenericFilterBean.java implementation class. 2) We can use the below utility class to write or print our error JSON model or String object into ServletResponse output stream。

public static void handleUnAuthorizedError(ServletResponse response,Exception e)
{
    ErrorModel error = null;
    if(e!=null)
        error = new ErrorModel(ErrorCodes.ACCOUNT_UNAUTHORIZED, e.getMessage());
    else
        error = new ErrorModel(ErrorCodes.ACCOUNT_UNAUTHORIZED, ApplicationConstants.UNAUTHORIZED);
    
    JsonUtils jsonUtils = new JsonUtils();
    HttpServletResponse httpResponse = (HttpServletResponse) response;
    httpResponse.setContentType(MediaType.APPLICATION_JSON_VALUE);
    httpResponse.setStatus(HttpServletResponse.SC_UNAUTHORIZED);
    try {
        httpResponse.getOutputStream().println(jsonUtils.convertToJSON(error));
    } catch (IOException ex) {
        ex.printStackTrace();
    }
}


public String convertToJSON(Object inputObj) {
        ObjectMapper objectMapper = new ObjectMapper();
        String orderJson = null;
        try {
            orderJson = objectMapper.writeValueAsString(inputObj);
        }
        catch(Exception e){
            e.printStackTrace();
        }
        return orderJson;
    }

It's strange because @ControllerAdvice should works, are you catching the correct Exception? 这很奇怪,因为@ControllerAdvice应该有效,你是否正在捕获正确的异常?

@ControllerAdvice
public class GlobalDefaultExceptionHandler {

    @ResponseBody
    @ExceptionHandler(value = DataAccessException.class)
    public String defaultErrorHandler(HttpServletResponse response, DataAccessException e) throws Exception {
       response.setStatus(HttpStatus.INTERNAL_SERVER_ERROR.value());
       //Json return
    }
}

Also try to catch this exception in CorsFilter and send 500 error, something like this 还尝试在CorsFilter中捕获此异常并发送500错误,类似这样

@ExceptionHandler(DataAccessException.class)
@ResponseBody
public String handleDataException(DataAccessException ex, HttpServletResponse response) {
    response.setStatus(HttpStatus.INTERNAL_SERVER_ERROR.value());
    //Json return
}

After reading through different methods suggested in the above answers, I decided to handle the authentication exceptions by using a custom filter. 在阅读上述答案中建议的不同方法后,我决定使用自定义过滤器处理身份验证异常。 I was able to handle the response status and codes using an error response class using the following method. 我能够使用以下方法使用错误响应类来处理响应状态和代码。

I created a custom filter and modified my security config by using the addFilterAfter method and added after the CorsFilter class. 我创建了一个自定义过滤器,并使用addFilterAfter方法修改了我的安全配置,并在CorsFilter类之后添加。

@Component
public class AuthFilter implements Filter {
@Override
public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws IOException, ServletException {
    //Cast the servlet request and response to HttpServletRequest and HttpServletResponse
    HttpServletResponse httpServletResponse = (HttpServletResponse) response;
    HttpServletRequest httpServletRequest = (HttpServletRequest) request;

    // Grab the exception from the request attribute
    Exception exception = (Exception) request.getAttribute("javax.servlet.error.exception");
    //Set response content type to application/json
    httpServletResponse.setContentType(MediaType.APPLICATION_JSON_VALUE);

    //check if exception is not null and determine the instance of the exception to further manipulate the status codes and messages of your exception
    if(exception!=null && exception instanceof AuthorizationParameterNotFoundException){
        ErrorResponse errorResponse = new ErrorResponse(exception.getMessage(),"Authetication Failed!");
        httpServletResponse.setStatus(HttpServletResponse.SC_UNAUTHORIZED);
        PrintWriter writer = httpServletResponse.getWriter();
        writer.write(convertObjectToJson(errorResponse));
        writer.flush();
        return;
    }
    // If exception instance cannot be determined, then throw a nice exception and desired response code.
    else if(exception!=null){
            ErrorResponse errorResponse = new ErrorResponse(exception.getMessage(),"Authetication Failed!");
            PrintWriter writer = httpServletResponse.getWriter();
            writer.write(convertObjectToJson(errorResponse));
            writer.flush();
            return;
        }
        else {
        // proceed with the initial request if no exception is thrown.
            chain.doFilter(httpServletRequest,httpServletResponse);
        }
    }

public String convertObjectToJson(Object object) throws JsonProcessingException {
    if (object == null) {
        return null;
    }
    ObjectMapper mapper = new ObjectMapper();
    return mapper.writeValueAsString(object);
}
}

SecurityConfig class SecurityConfig类

    @Configuration
    public class JwtSecurityConfig extends WebSecurityConfigurerAdapter {
    @Autowired
    AuthFilter authenticationFilter;
    @Override
    protected void configure(HttpSecurity http) throws Exception {
        http.addFilterAfter(authenticationFilter, CorsFilter.class).csrf().disable()
                .cors(); //........
        return http;
     }
   }

ErrorResponse class ErrorResponse类

public class ErrorResponse  {
private final String message;
private final String description;

public ErrorResponse(String description, String message) {
    this.message = message;
    this.description = description;
}

public String getMessage() {
    return message;
}

public String getDescription() {
    return description;
}}

Late to the party but we can also use it like this:迟到了,但我们也可以这样使用它:

@ApiIgnore
@RestControllerAdvice
public class ExceptionHandlerController {

    @Autowired
    private MessageSource messageSource;

And in the filter:在过滤器中:

@Component
public class MyFilter extends OncePerRequestFilter {

    @Autowired
    @Qualifier("handlerExceptionResolver")
    private HandlerExceptionResolver exceptionResolver;


    @Override
    protected void doFilterInternal(HttpServletRequest request, @NotNull HttpServletResponse response, @NotNull FilterChain filterChain) {
            try {
              // Some exception
            } catch (Exception e) {
                this.exceptionResolver.resolveException(request, response, null, e);
            }
        }

Global Default Exception Handlers will work only at Controller or Service level.全局默认异常处理程序将仅在 Controller 或服务级别工作。 They will not work at filter level.它们不会在过滤器级别工作。 I found below solution working fine with Spring Boot Security - JWT filter我发现以下解决方案与 Spring Boot Security - JWT 过滤器配合使用效果很好

https://www.jvt.me/posts/2022/01/17/spring-servlet-filter-error-handling/ https://www.jvt.me/posts/2022/01/17/spring-servlet-filter-error-handling/

Below is the snippet I added下面是我添加的片段

    httpServletResponse.setContentType("application/json");
    httpServletResponse.setStatus(HttpServletResponse.SC_UNAUTHORIZED);
    httpServletResponse.getWriter().write("{\"error\":\"invalid_token\",\"error_description\":\"Invalid Token\"}");

You do not need to create a custom Filter for this.您不需要为此创建自定义过滤器。 We solved this by creating custom exceptions that extend ServletException (which is thrown from the doFilter method, shown in the declaration).我们通过创建扩展 ServletException 的自定义异常(从 doFilter 方法抛出,如声明中所示)解决了这个问题。 These are then caught and handled by our global error handler.然后我们的全局错误处理程序会捕获并处理这些错误。

edit: grammar编辑:语法

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM