Пользовательская обработка исключений с помощью Spring Boot 2.0.6.RELEASE + Spring Security - не работает - PullRequest
0 голосов
/ 13 июля 2020

Я создаю веб-службу 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 (...); , и мне нужно показать их в случае сбоя.

Пожалуйста, сообщите, есть ли какие-либо проблемы в конфигурации или любые другие работать вокруг. Спасибо.

...