Я создаю веб-службу REST, которая использует @RestControllerAdvice для пользовательской обработки исключений + безопасность Spring для @PreAuthorize + HandlerInterceptor для ведения журнала + GenericFilterBean для обеспечения соблюдения JWT.
У меня есть пользовательский обработчик исключений, который я используется для создания исключений во время потока запроса. Проблема, с которой я столкнулся, заключается в том, что в ответе не отображается json, вместо этого он ничего не отображает, кроме кода ответа, который я бросаю в настраиваемое исключение. Это происходит, когда настраиваемое исключение выбрасывается в GenericFilterBean и код ответа 401, 404, 403
Если я использую весеннюю загрузочную версию 1.5.6.RELEASE, тогда все работает хорошо. Если я переключусь на 2.0.6.RELEASE, проблема начнет возникать. Если я удалю безопасность Spring, все снова будет работать хорошо, но мне нужна безопасность Spring.
Ниже приведены мои конфигурации и образцы кода, GenericFilterBean
public class JwtTokenFilter extends GenericFilterBean {
private JwtTokenProvider jwtTokenProvider;
public JwtTokenFilter(JwtTokenProvider jwtTokenProvider) {
this.jwtTokenProvider = jwtTokenProvider;
}
@Override
public void doFilter(ServletRequest req, ServletResponse res, FilterChain filterChain)
throws IOException, ServletException {
HttpServletRequest request = (HttpServletRequest) req;
HttpServletResponse response = (HttpServletResponse) res;
try {
if (request.getRequestURI().contains("/signup")) {
String token = jwtTokenProvider.resolveSignupToken((HttpServletRequest) req);
//Add a specific filtering for signup call only to decode signup jwt token..
if (token != null && !token.isEmpty()) {
try {
jwtTokenProvider.validateSignupToken(token);
} catch (JwtException | IllegalArgumentException e) {
response.sendError(HttpServletResponse.SC_UNAUTHORIZED, "Invalid JWT token");
throw new CustomException("Expired or invalid JWT token", HttpStatus.UNAUTHORIZED, "01");
}
Authentication auth = token != null ? jwtTokenProvider.getSignUpAuthentication(token) : null;
//setting auth in the context.
SecurityContextHolder.getContext().setAuthentication(auth);
}
} else {
String token = jwtTokenProvider.resolveToken((HttpServletRequest) req);
//Add a specific filtering for signup call only to decode signup jwt token..
if (token != null && !token.isEmpty()) {
Authentication auth = token != null ? jwtTokenProvider.getAuthentication(token) : null;
//setting auth in the context.
SecurityContextHolder.getContext().setAuthentication(auth);
}
}
} catch (CustomException ex) {
HttpServletResponse excpResponse = (HttpServletResponse) response;
excpResponse.sendError(ex.getHttpStatus().value(), ex.getMessage());
return;
} finally {
ThreadContext.push("Host:" + request.getRemoteHost());
filterChain.doFilter(req, res);
}
}}
Пользовательский класс исключения
package com.rockville.butik.wallet.mms.ws.exception;
import org.springframework.http.HttpStatus;
public class CustomException extends RuntimeException {
private static final long serialVersionUID = 1L;
private final String message;
private final String respCode;
private final HttpStatus httpStatus;
public CustomException(String message, HttpStatus httpStatus, String respCode) {
super();
this.message = message;
this.httpStatus = httpStatus;
this.respCode = respCode;
}
@Override
public String getMessage() {
return message;
}
public HttpStatus getHttpStatus() {
return httpStatus;
}
public String getRespCode() {
return respCode;
}}
GlobalExceptionHandlerController
package com.rockville.butik.wallet.mms.ws.exception;
import org.springframework.http.HttpStatus;
import org.springframework.security.access.AccessDeniedException;
import org.springframework.web.bind.annotation.ExceptionHandler;
import org.springframework.web.bind.annotation.RestControllerAdvice;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.util.Map;
import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;
import org.springframework.boot.web.servlet.error.DefaultErrorAttributes;
import org.springframework.boot.web.servlet.error.ErrorAttributes;
import org.springframework.context.annotation.Bean;
import org.springframework.web.context.request.WebRequest;
@RestControllerAdvice
public class GlobalExceptionHandlerController {
private static final Logger LOG = LogManager.getLogger(GlobalExceptionHandlerController.class);
@Bean
public ErrorAttributes errorAttributes() {
// Hide exception field in the return object
return new DefaultErrorAttributes() {
@Override
public Map<String, Object> getErrorAttributes(WebRequest requestAttributes, boolean includeStackTrace) {
Map<String, Object> errorAttributes = super.getErrorAttributes(requestAttributes, includeStackTrace);
errorAttributes.remove("exception");
return errorAttributes;
}
};
}
@ExceptionHandler(CustomException.class)
public void handleCustomException(HttpServletResponse res, CustomException e) throws IOException {
LOG.error("ERROR", e);
res.sendError(e.getHttpStatus().value(), e.getMessage());
}
@ExceptionHandler(AccessDeniedException.class)
public void handleAccessDeniedException(HttpServletResponse res, AccessDeniedException e) throws IOException {
LOG.error("ERROR", e);
res.sendError(HttpStatus.FORBIDDEN.value(), "Access denied");
}
@ExceptionHandler(IllegalArgumentException.class)
public void handleIllegalArgumentException(HttpServletResponse res, IllegalArgumentException e) throws IOException {
LOG.error("ERROR", e);
res.sendError(HttpStatus.BAD_REQUEST.value(), "Something went wrong");
}
@ExceptionHandler(Exception.class)
public void handleException(HttpServletResponse res, Exception e) throws IOException {
LOG.error("ERROR", e);
res.sendError(HttpStatus.BAD_REQUEST.value(), "Something went wrong");
}
}
POM
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<groupId>test</groupId>
<artifactId>test</artifactId>
<version>1.0.0</version>
<packaging>jar</packaging>
<name>test</name>
<properties>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
<project.reporting.outputEncoding>UTF-8</project.reporting.outputEncoding>
<java.version>1.8</java.version>
<start-class>com.test.ApplicationStartup</start-class>
<spring-cloud.version>Finchley.SR1</spring-cloud.version>
</properties>
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>2.0.6.RELEASE</version>
<relativePath /> <!-- lookup parent from repository -->
</parent>
<dependencies>
<dependency>
<!-- Setup Spring Boot -->
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
<!-- JPA Data (Repositories, Entities, Hibernate, etc..) -->
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-jpa</artifactId>
<exclusions>
<exclusion>
<groupId>org.apache.tomcat</groupId>
<artifactId>tomcat-jdbc</artifactId>
</exclusion>
</exclusions>
</dependency>
<dependency>
<groupId>com.zaxxer</groupId>
<artifactId>HikariCP</artifactId>
</dependency>
<dependency>
<!-- Starter for using Spring Security -->
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-security</artifactId>
</dependency>
<dependency>
<!-- Make method based security testing easier -->
<groupId>org.springframework.security</groupId>
<artifactId>spring-security-test</artifactId>
<scope>test</scope>
</dependency>
<!-- <dependency>
Automatically restart whenever files on the classpath change
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-devtools</artifactId>
<optional>true</optional>
</dependency>-->
<dependency>
<!-- Use MySQL Connector-J -->
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
<version>8.0.16</version>
</dependency>
<dependency>
<groupId>javax.xml.bind</groupId>
<artifactId>jaxb-api</artifactId>
<version>2.3.0</version>
</dependency>
<dependency>
<groupId>com.h2database</groupId>
<artifactId>h2</artifactId>
<scope>runtime</scope>
</dependency>
<dependency>
<!-- Automated JSON API documentation for API's built with Spring -->
<groupId>io.springfox</groupId>
<artifactId>springfox-swagger2</artifactId>
<version>2.7.0</version>
</dependency>
<dependency>
<!-- Generate beautiful documentation from a Swagger-compliant API. -->
<groupId>io.springfox</groupId>
<artifactId>springfox-swagger-ui</artifactId>
<version>2.7.0</version>
</dependency>
<dependency>
<!-- JSON Web Token Support -->
<groupId>io.jsonwebtoken</groupId>
<artifactId>jjwt</artifactId>
<version>0.7.0</version>
</dependency>
<dependency>
<!-- Model Mapper -->
<groupId>org.modelmapper</groupId>
<artifactId>modelmapper</artifactId>
<version>1.1.0</version>
</dependency>
<dependency>
<groupId>com.whalin</groupId>
<artifactId>Memcached-Java-Client</artifactId>
<version>3.0.2</version>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
<exclusions>
<exclusion>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-logging</artifactId>
</exclusion>
</exclusions>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-log4j2</artifactId>
</dependency>
<dependency>
<groupId>com.sun.jersey</groupId>
<artifactId>jersey-client</artifactId>
<!--<version>1.9.1</version>-->
</dependency>
<dependency>
<groupId>com.google.code.gson</groupId>
<artifactId>gson</artifactId>
<!--<version>2.7</version>-->
<type>jar</type>
</dependency>
<dependency>
<groupId>org.apache.commons</groupId>
<artifactId>commons-io</artifactId>
<version>1.3.2</version>
</dependency>
<dependency>
<groupId>com.googlecode.json-simple</groupId>
<artifactId>json-simple</artifactId>
<version>1.1</version>
</dependency>
<dependency>
<groupId>org.bouncycastle</groupId>
<artifactId>bcprov-jdk15on</artifactId>
<version>1.55</version>
<type>jar</type>
</dependency>
<!-- AutoScaling Dependencies -->
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-netflix-eureka-client</artifactId>
<!--<version>1.4.6.RELEASE</version>-->
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-actuator</artifactId>
<!--<version>1.5.19.RELEASE</version>-->
</dependency>
</dependencies>
<dependencyManagement>
<dependencies>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-dependencies</artifactId>
<version>${spring-cloud.version}</version>
<type>pom</type>
<scope>import</scope>
</dependency>
</dependencies>
</dependencyManagement>
<!-- Export the jar one time, this will reduce module jar size -->
<build>
<plugins>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-jar-plugin</artifactId>
<version>2.4</version>
<configuration>
<archive>
<manifest>
<mainClass>com.test.ApplicationStartup</mainClass>
<addClasspath>true</addClasspath>
<classpathPrefix>dependency-jars/</classpathPrefix>
</manifest>
</archive>
</configuration>
</plugin>
<plugin>
<artifactId>maven-dependency-plugin</artifactId>
<executions>
<execution>
<phase>package</phase>
<goals>
<goal>copy-dependencies</goal>
</goals>
<configuration>
<outputDirectory>${project.build.directory}/dependency-jars/</outputDirectory>
</configuration>
</execution>
</executions>
</plugin>
<plugin>
<artifactId>maven-resources-plugin</artifactId>
<version>2.6</version>
<executions>
<execution>
<id>copy-resources</id>
<phase>validate</phase>
<goals>
<goal>copy-resources</goal>
</goals>
<configuration>
<outputDirectory>${basedir}/target/</outputDirectory>
<resources>
<resource>
<directory>src/main/resources</directory>
<includes>
<include>*.properties</include>
<include>*.yml</include>
</includes>
</resource>
</resources>
</configuration>
</execution>
</executions>
</plugin>
</plugins>
</build>
<repositories>
<repository>
<id>central</id>
<name>Central Repository</name>
<url>https://repo.maven.apache.org/maven2</url>
<layout>default</layout>
<snapshots>
<enabled>false</enabled>
</snapshots>
</repository>
</repositories>
</project>
при загрузке Spring 1.5.6 сопоставление / error легко отключить и не принудительно, но в Spring 2.0.6 я перепробовал все доступные способы, добавив свойства для его отключения и et c, но ничего не работает. Я также пробовал Контроллер для / error , но он мне не подходил. Система заполнена throw new CustomException (...); , и мне нужно показать их в случае сбоя.
Пожалуйста, сообщите, есть ли какие-либо проблемы в конфигурации или любые другие работать вокруг. Спасибо.