json - Spring MVC(或 Spring Boot)。针对安全相关异常的自定义 JSON 响应,例如 401 Unauthorized 或 403 Forbidden)

标签 json spring spring-mvc spring-boot spring-security

我正在开发 REST 服务。它使用 JSON,并且在出现问题时必须返回一些预定义的 JSON 对象。默认的 Spring 响应如下所示:

{
  "timestamp": 1512578593776,
  "status": 403,
  "error": "Forbidden",
  "message": "Access Denied",
  "path": "/swagger-ui.html"
}

我想用自己的 JSON 替换这个默认 JSON(带有堆栈跟踪和其他异常相关信息)。

Spring 提供了一种方便的方法来覆盖默认行为。应该定义一个 @RestControllerAdvice带有自定义异常处理程序的 bean。像这样
@RestControllerAdvice
public class GlobalExceptionHandler {
  @ExceptionHandler(value = {Exception.class})
  public ResponseEntity<ExceptionResponse> unknownException(Exception ex) {
    ExceptionResponse resp = new ExceptionResponse(ex, level); // my custom response object
    return new ResponseEntity<ExceptionResponse>(resp, resp.getStatus());
  }
  @ExceptionHandler(value = {AuthenticationException.class})
  public ResponseEntity<ExceptionResponse> authenticationException(AuthenticationExceptionex) {
      // WON'T WORK
  }
}

定制ExceptionResponse然后 Spring 将使用特殊的消息转换器将对象转换为 JSON。

问题是 , 安全异常如 InsufficientAuthenticationException不能被注释为 @ExceptionHandler 的方法拦截.这种异常发生在 Spring MVC 调度程序 servlet 被输入和所有 MVC 处理程序被初始化之前。

可以使用自定义过滤器拦截此异常并从头开始构建自己的 JSON 序列化。在这种情况下,我们会得到一个完全独立于 Spring MVC 基础设施其余部分的代码。这不好。

我找到的解决方案似乎有效,但看起来很疯狂。
@Configuration
public class CustomSecurityConfiguration extends 
WebSecurityConfigurerAdapter {

@Autowired
protected RequestMappingHandlerAdapter requestMappingHandlerAdapter;

@Autowired
protected GlobalExceptionHandler exceptionHandler;

@Override
protected void configure(HttpSecurity http) throws Exception {

    http.authorizeRequests()
        .anyRequest()
        .fullyAuthenticated();

    http.exceptionHandling()
        .authenticationEntryPoint(authenticationEntryPoint());
}

public AuthenticationEntryPoint authenticationEntryPoint() {
    return new AuthenticationEntryPoint() {
        @Override
        public void commence(HttpServletRequest request, HttpServletResponse response,
                AuthenticationException authException) throws IOException, ServletException {

            try {
                ResponseEntity<ExceptionResponse> objResponse = exceptionHandler.authenticationException(authException);

                Method unknownException = exceptionHandler.getClass().getMethod("authenticationException", AuthenticationException.class);

                HandlerMethod handlerMethod = new HandlerMethod(exceptionHandler, unknownException);

                MethodParameter returnType = handlerMethod.getReturnValueType(objResponse);

                ModelAndViewContainer mvc = new ModelAndViewContainer(); // not really used here.

                List<HttpMessageConverter<?>> mconverters = requestMappingHandlerAdapter.getMessageConverters();

                DispatcherServletWebRequest webRequest = new DispatcherServletWebRequest(request, response);

                HttpEntityMethodProcessor processor = new HttpEntityMethodProcessor(mconverters);

                processor.handleReturnValue(objResponse, returnType, mvc, webRequest);
            } catch (IOException e) {
                throw e;
            } catch (RuntimeException e) {
                throw e;                    
            } catch (Exception e) {
                throw new ServletException(e);
            }
        }
    };
}

有没有办法使用看起来比这更好的 Spring 序列化管道(在消息转换器中使用 Spring 构建、MIME 格式协商等)?

最佳答案

创建 Bean 类

@Component public class AuthenticationExceptionHandler implements AuthenticationEntryPoint, Serializable

并覆盖 commence() method并使用对象映射器创建一个 json 响应,如下例所示
 ObjectMapper mapper = new ObjectMapper();
 String responseMsg = mapper.writeValueAsString(responseObject);
 response.getWriter().write(responseMsg);

@Autowire AuthenticationExceptionHandlerSecurityConfiguration类(class)和在 configure(HttpSecurity http) method添加以下行
        http.exceptionHandling()
            .authenticationEntryPoint(authenticationExceptionHandler) 

这样,您应该能够发送自定义 json 响应 401/403。同上,您可以使用AccessDeniedHandler .如果这有助于您解决问题,请告诉我们。

关于json - Spring MVC(或 Spring Boot)。针对安全相关异常的自定义 JSON 响应,例如 401 Unauthorized 或 403 Forbidden),我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/47679897/

相关文章:

asp.net-mvc - 不允许使用用于访问路径'/Documents/TestNote/Documents/AddNote'的HTTP动词POST

asp.net - JqG​​rid 在 ASP.NET MVC2 中不起作用

java - Spring 休息-发布文件

java - 如何使用 ITextRenderer 在每个页面上添加页眉和页脚

javascript - 在 JavaScript 中按属性过滤 JSON 数据

ios - 将字符串从 JSON 转换为 UIImage

java - 了解 Spring Boot

java - 使用 Spring WebClient 发出多个请求

java - 服务中的服务

java - JSONObject 响应比较