From c2c47212e5774cb374b6cf20f127f80f482abcd6 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Johnny=20Miller=20=28=E9=94=BA=E4=BF=8A=29?= Date: Mon, 21 Dec 2020 11:39:03 +0800 Subject: [PATCH] feat($gateway): make Swagger and Spring Security work together --- .../MethodArgumentValidationAspect.java | 160 ------------------ .../universal/aspect/ValidateArgument.java | 19 --- .../configuration/CustomConfiguration.java | 8 +- .../CustomServerAccessDeniedHandler.java | 26 +++ .../ServerAuthenticationEntryPointImpl.java | 26 +++ .../SwaggerResourceProvider.java | 68 +++++--- .../WebFluxSecurityConfiguration.java | 29 ++-- .../universal/filter/AuthGlobalFilter.java | 24 --- .../universal/filter/RequestFilter.java | 26 +-- .../service/impl/CommonServiceImpl.java | 2 - .../gateway/universal/util/ResponseUtil.java | 48 ++++++ .../application-development-docker.yml | 27 ++- .../application-development-local.yml | 7 +- .../main/resources/application-production.yml | 27 ++- .../src/main/resources/application-stage.yml | 27 ++- .../src/main/resources/application-test.yml | 27 ++- 16 files changed, 285 insertions(+), 266 deletions(-) delete mode 100644 gateway/src/main/java/com/jmsoftware/maf/gateway/universal/aspect/MethodArgumentValidationAspect.java delete mode 100644 gateway/src/main/java/com/jmsoftware/maf/gateway/universal/aspect/ValidateArgument.java create mode 100644 gateway/src/main/java/com/jmsoftware/maf/gateway/universal/configuration/CustomServerAccessDeniedHandler.java create mode 100644 gateway/src/main/java/com/jmsoftware/maf/gateway/universal/configuration/ServerAuthenticationEntryPointImpl.java delete mode 100644 gateway/src/main/java/com/jmsoftware/maf/gateway/universal/filter/AuthGlobalFilter.java create mode 100644 gateway/src/main/java/com/jmsoftware/maf/gateway/universal/util/ResponseUtil.java diff --git a/gateway/src/main/java/com/jmsoftware/maf/gateway/universal/aspect/MethodArgumentValidationAspect.java b/gateway/src/main/java/com/jmsoftware/maf/gateway/universal/aspect/MethodArgumentValidationAspect.java deleted file mode 100644 index 672a5cc1..00000000 --- a/gateway/src/main/java/com/jmsoftware/maf/gateway/universal/aspect/MethodArgumentValidationAspect.java +++ /dev/null @@ -1,160 +0,0 @@ -package com.jmsoftware.maf.gateway.universal.aspect; - -import cn.hutool.core.collection.CollUtil; -import cn.hutool.core.util.ArrayUtil; -import cn.hutool.core.util.StrUtil; -import lombok.extern.slf4j.Slf4j; -import lombok.val; -import org.aspectj.lang.JoinPoint; -import org.aspectj.lang.ProceedingJoinPoint; -import org.aspectj.lang.annotation.*; -import org.aspectj.lang.reflect.MethodSignature; -import org.springframework.stereotype.Component; - -import javax.validation.ConstraintViolation; -import javax.validation.Valid; -import javax.validation.Validation; -import javax.validation.Validator; -import java.util.LinkedList; -import java.util.Set; - -/** - *

MethodArgumentValidationAspect

- *

This class is an aspect class that validates method's argument(s).

- *

USAGE (Must Do's)

- *
    - *
  1. Annotate the method which argument we need to validate by @ValidateArgument
  2. - *
  3. Annotate the argument(s) that we need to validate by @javax.validation.Valid
  4. - *
  5. The field(s) of the argument(s) could be annotated by the constraint annotation provided by Spring - * Security
  6. - *
- *

ATTENTION

- *

If the argument doesn't pass validation, an IllegalArgumentException will be thrown, and not proceed - * the target method.

- * - * @author Johnny Miller (锺俊), email: johnnysviva@outlook.com - * @date 2019-07-06 12:17 - **/ -@Slf4j -@Aspect -@Component -public class MethodArgumentValidationAspect { - private final Validator validator; - - public MethodArgumentValidationAspect() { - val validatorFactory = Validation.buildDefaultValidatorFactory(); - this.validator = validatorFactory.getValidator(); - log.info("The validator for {} has been initiated.", this.getClass().getSimpleName()); - } - - /** - * Define pointcut. Pointcut is a predicate or expression that matches join points. In - * ValidateMethodArgumentAspect, we need to cut any method annotated with `@ValidateArgument` only. - *

- * More detail at: Spring aop aspectJ - * pointcut expression examples - */ - @Pointcut("@annotation(com.jmsoftware.maf.gateway.universal.aspect.ValidateArgument)") - public void validateMethodArgumentPointcut() { - } - - /** - * Before handle method's argument. This method will be executed after called `proceedingJoinPoint.proceed()`. In - * this phrase, we're going to take some logs. - *

- * `@Before` annotated methods run exactly before the all methods matching with pointcut expression. - * - * @param joinPoint a point of execution of the program - */ - @Before("validateMethodArgumentPointcut()") - public void beforeMethodHandleArgument(JoinPoint joinPoint) { - log.info("Method : {}#{}", - joinPoint.getSignature().getDeclaringTypeName(), - joinPoint.getSignature().getName()); - log.info("Argument : {}", joinPoint.getArgs()); - } - - /** - * Around annotated method processes argument. Around advice can perform custom behavior before and after the - * method invocation. - * - * @param proceedingJoinPoint the object can perform method invocation - * @return any value (may be void) that annotated method returned - */ - @Around("validateMethodArgumentPointcut()") - public Object aroundMethodHandleArgument(ProceedingJoinPoint proceedingJoinPoint) throws Throwable { - log.info("======= METHOD'S ARGUMENT VALIDATION START ======="); - val args = proceedingJoinPoint.getArgs(); - val signature = (MethodSignature) proceedingJoinPoint.getSignature(); - val parameterAnnotations = signature.getMethod().getParameterAnnotations(); - // argumentIndexes is the array list that stores the index of argument we need to validate (the argument - // annotated by `@Valid`) - val argumentIndexListThatNeedsToBeValidated = new LinkedList(); - for (val parameterAnnotation : parameterAnnotations) { - val paramIndex = ArrayUtil.indexOf(parameterAnnotations, parameterAnnotation); - for (val annotation : parameterAnnotation) { - if (annotation instanceof Valid) { - argumentIndexListThatNeedsToBeValidated.add(paramIndex); - } - } - } - val errorMessageList = new LinkedList(); - for (val index : argumentIndexListThatNeedsToBeValidated) { - val constraintViolationSet = validator.validate(args[index]); - if (CollUtil.isNotEmpty(constraintViolationSet)) { - val errorMessage = String.format("Argument validation failed! %s", - getAllFieldErrorMessage(constraintViolationSet)); - errorMessageList.add(errorMessage); - } - } - if (CollUtil.isNotEmpty(errorMessageList)) { - val joinedErrorMessage = StrUtil.join(", ", errorMessageList); - log.info("Method : {}#{}", proceedingJoinPoint.getSignature().getDeclaringTypeName(), - proceedingJoinPoint.getSignature().getName()); - log.info("Argument : {}", args); - log.error("Validation result: {}", joinedErrorMessage); - // If the argument doesn't pass validation, an IllegalArgumentException will be thrown, and not - // proceed the target method - throw new IllegalArgumentException(joinedErrorMessage); - } - log.info("Validation result: Validation passed"); - return proceedingJoinPoint.proceed(); - } - - /** - * `@After` annotated methods run exactly after the all methods matching with pointcut expression. - */ - @After("validateMethodArgumentPointcut()") - public void afterMethodHandleArgument() { - log.info("======== METHOD'S ARGUMENT VALIDATION END ========"); - } - - /** - * `@AfterThrowing` annotated methods run after the method (matching with pointcut expression) exits by throwing an - * exception. - * - * @param joinPoint a point of execution of the program - * @param e exception that controller's method throws - */ - @AfterThrowing(pointcut = "validateMethodArgumentPointcut()", throwing = "e") - public void afterThrowingException(JoinPoint joinPoint, Exception e) { - log.info("Signature : {}", joinPoint.getSignature().toShortString()); - log.error("Exception message: {}", e.toString()); - log.error("== METHOD'S ARGUMENT VALIDATION END WITH EXCEPTION =="); - } - - /** - * Gets all field error message. - * - * @param constraintViolationSet the constraint violation set - * @return the all field error message - */ - private String getAllFieldErrorMessage(Set> constraintViolationSet) { - val allErrorMessageList = new LinkedList(); - for (val constraintViolation : constraintViolationSet) { - allErrorMessageList.add(String.format("invalid field: %s, %s", constraintViolation.getPropertyPath(), - constraintViolation.getMessage())); - } - return StrUtil.join("; ", allErrorMessageList); - } -} diff --git a/gateway/src/main/java/com/jmsoftware/maf/gateway/universal/aspect/ValidateArgument.java b/gateway/src/main/java/com/jmsoftware/maf/gateway/universal/aspect/ValidateArgument.java deleted file mode 100644 index 4e105fb4..00000000 --- a/gateway/src/main/java/com/jmsoftware/maf/gateway/universal/aspect/ValidateArgument.java +++ /dev/null @@ -1,19 +0,0 @@ -package com.jmsoftware.maf.gateway.universal.aspect; - -import java.lang.annotation.*; - -/** - *

ValidateArgument

- *

Annotation for validating method's argument.

- *

ATTENTION

- *

If the argument doesn't pass validation, an IllegalArgumentException will be thrown, and not proceed - * the target method.

- * - * @author Johnny Miller (锺俊), email: johnnysviva@outlook.com - * @date 2019-07-06 12:08 - **/ -@Retention(RetentionPolicy.RUNTIME) -@Target({ElementType.METHOD}) -@Documented -public @interface ValidateArgument { -} diff --git a/gateway/src/main/java/com/jmsoftware/maf/gateway/universal/configuration/CustomConfiguration.java b/gateway/src/main/java/com/jmsoftware/maf/gateway/universal/configuration/CustomConfiguration.java index c6450dc5..826d996e 100644 --- a/gateway/src/main/java/com/jmsoftware/maf/gateway/universal/configuration/CustomConfiguration.java +++ b/gateway/src/main/java/com/jmsoftware/maf/gateway/universal/configuration/CustomConfiguration.java @@ -1,12 +1,11 @@ package com.jmsoftware.maf.gateway.universal.configuration; -import com.google.common.collect.Lists; import lombok.Data; import org.springframework.boot.context.properties.ConfigurationProperties; import org.springframework.stereotype.Component; import org.springframework.validation.annotation.Validated; -import java.util.List; +import javax.validation.constraints.NotBlank; /** *

CustomConfiguration

@@ -21,15 +20,12 @@ @Component @ConfigurationProperties(prefix = "custom.configuration") public class CustomConfiguration { - /** - * The Allowed application list. If it's empty, gateway will allow all request to any applications (microservices) - */ - private List allowedApplicationList = Lists.newLinkedList(); /** *

The username of super user who has no restriction to access any system's resources.

*

ATTENTION: The value of username of super user must be equal to the value that is * persistent in database.

*/ + @NotBlank private String superUser; /** * Ignore URLs diff --git a/gateway/src/main/java/com/jmsoftware/maf/gateway/universal/configuration/CustomServerAccessDeniedHandler.java b/gateway/src/main/java/com/jmsoftware/maf/gateway/universal/configuration/CustomServerAccessDeniedHandler.java new file mode 100644 index 00000000..775e780a --- /dev/null +++ b/gateway/src/main/java/com/jmsoftware/maf/gateway/universal/configuration/CustomServerAccessDeniedHandler.java @@ -0,0 +1,26 @@ +package com.jmsoftware.maf.gateway.universal.configuration; + +import com.jmsoftware.maf.gateway.universal.util.ResponseUtil; +import lombok.extern.slf4j.Slf4j; +import org.springframework.context.annotation.Configuration; +import org.springframework.http.HttpStatus; +import org.springframework.security.access.AccessDeniedException; +import org.springframework.security.web.server.authorization.ServerAccessDeniedHandler; +import org.springframework.web.server.ServerWebExchange; +import reactor.core.publisher.Mono; + +/** + * Description: CustomServerAccessDeniedHandler, change description here. + * + * @author 钟俊(zhongjun), email: zhongjun@toguide.cn, date: 12/21/2020 10:12 AM + **/ +@Slf4j +@Configuration +public class CustomServerAccessDeniedHandler implements ServerAccessDeniedHandler { + @Override + public Mono handle(ServerWebExchange exchange, AccessDeniedException denied) { + log.error("Access denied! Exception message: {}. Request URL: [{}] {}", denied.getMessage(), + exchange.getRequest().getMethod(), exchange.getRequest().getURI()); + return ResponseUtil.renderJson(exchange, HttpStatus.FORBIDDEN, null); + } +} diff --git a/gateway/src/main/java/com/jmsoftware/maf/gateway/universal/configuration/ServerAuthenticationEntryPointImpl.java b/gateway/src/main/java/com/jmsoftware/maf/gateway/universal/configuration/ServerAuthenticationEntryPointImpl.java new file mode 100644 index 00000000..e99887e6 --- /dev/null +++ b/gateway/src/main/java/com/jmsoftware/maf/gateway/universal/configuration/ServerAuthenticationEntryPointImpl.java @@ -0,0 +1,26 @@ +package com.jmsoftware.maf.gateway.universal.configuration; + +import com.jmsoftware.maf.gateway.universal.util.ResponseUtil; +import lombok.extern.slf4j.Slf4j; +import org.springframework.context.annotation.Configuration; +import org.springframework.http.HttpStatus; +import org.springframework.security.core.AuthenticationException; +import org.springframework.security.web.server.ServerAuthenticationEntryPoint; +import org.springframework.web.server.ServerWebExchange; +import reactor.core.publisher.Mono; + +/** + * Description: ServerAuthenticationEntryPointImpl, change description here. + * + * @author 钟俊(zhongjun), email: zhongjun@toguide.cn, date: 12/21/2020 9:48 AM + **/ +@Slf4j +@Configuration +public class ServerAuthenticationEntryPointImpl implements ServerAuthenticationEntryPoint { + @Override + public Mono commence(ServerWebExchange serverWebExchange, AuthenticationException e) { + log.error("Exception occurred when authenticating! Exception message: {}. Request URL: [{}] {}", e.getMessage(), + serverWebExchange.getRequest().getMethod(), serverWebExchange.getRequest().getURI()); + return ResponseUtil.renderJson(serverWebExchange, HttpStatus.NETWORK_AUTHENTICATION_REQUIRED, null); + } +} diff --git a/gateway/src/main/java/com/jmsoftware/maf/gateway/universal/configuration/SwaggerResourceProvider.java b/gateway/src/main/java/com/jmsoftware/maf/gateway/universal/configuration/SwaggerResourceProvider.java index 113ff326..39d22e65 100644 --- a/gateway/src/main/java/com/jmsoftware/maf/gateway/universal/configuration/SwaggerResourceProvider.java +++ b/gateway/src/main/java/com/jmsoftware/maf/gateway/universal/configuration/SwaggerResourceProvider.java @@ -1,15 +1,20 @@ package com.jmsoftware.maf.gateway.universal.configuration; -import cn.hutool.core.collection.CollUtil; -import cn.hutool.core.util.StrUtil; -import cn.hutool.json.JSONUtil; import lombok.RequiredArgsConstructor; import lombok.extern.slf4j.Slf4j; import lombok.val; import org.springframework.cloud.gateway.config.GatewayProperties; import org.springframework.cloud.gateway.route.RouteLocator; +import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Primary; import org.springframework.stereotype.Component; +import springfox.documentation.builders.ApiInfoBuilder; +import springfox.documentation.builders.PathSelectors; +import springfox.documentation.builders.RequestHandlerSelectors; +import springfox.documentation.service.ApiInfo; +import springfox.documentation.service.Contact; +import springfox.documentation.spi.DocumentationType; +import springfox.documentation.spring.web.plugins.Docket; import springfox.documentation.swagger.web.SwaggerResource; import springfox.documentation.swagger.web.SwaggerResourcesProvider; @@ -35,11 +40,10 @@ @Component @RequiredArgsConstructor public class SwaggerResourceProvider implements SwaggerResourcesProvider { - public static final String SWAGGER_API_URI = "/v2/api-docs"; - private final RouteLocator routeLocator; - private final GatewayProperties gatewayProperties; + private static final String SWAGGER_API_URI = "/v2/api-docs"; + private static final String LINE_SEPARATOR = System.lineSeparator(); private final ProjectProperty projectProperty; - private final CustomConfiguration customConfiguration; + private final RouteLocator routeLocator; /** * Generate Swagger resource. @@ -53,26 +57,48 @@ public List get() { val swaggerResourceList = new LinkedList(); routeLocator.getRoutes().subscribe(route -> { val serviceName = route.getUri().toString().substring(5).toLowerCase(); - log.info("{} found dynamic route for [{}] from subscription. {}", projectProperty.getProjectArtifactId(), + log.warn("{} found dynamic route. Service name: {}, route: {}", this.getClass().getSimpleName(), serviceName, route); val swaggerResource = new SwaggerResource(); swaggerResource.setName(serviceName.toUpperCase()); swaggerResource.setLocation(String.format("%s%s", serviceName, SWAGGER_API_URI)); swaggerResource.setSwaggerVersion("2.0"); - log.info("Got allowed application list: {}", customConfiguration.getAllowedApplicationList()); - if (CollUtil.isEmpty(customConfiguration.getAllowedApplicationList())) { - log.warn("Allowed application list is not configured. Swagger is able to access any applications."); - swaggerResourceList.add(swaggerResource); - } else { - customConfiguration.getAllowedApplicationList().forEach(allocationName -> { - if (StrUtil.compareIgnoreCase(serviceName, allocationName, false) == 0) { - log.warn("Swagger is adding resource. {}", JSONUtil.toJsonStr(swaggerResource)); - swaggerResourceList.add(swaggerResource); - } - }); - } + log.warn("Exposed Swagger Resource: {}", swaggerResource.toString()); + swaggerResourceList.add(swaggerResource); }); - log.info("Pre defined GatewayProperties. {}", gatewayProperties); return swaggerResourceList; } + + @Bean + public Docket createRestApi() { + return new Docket(DocumentationType.SWAGGER_2) + .apiInfo(apiInfo()) + .select() + .apis(RequestHandlerSelectors.basePackage(projectProperty.getBasePackage())) + .paths(PathSelectors.any()) + .build(); + } + + private ApiInfo apiInfo() { + val projectArtifactId = projectProperty.getProjectArtifactId(); + val version = projectProperty.getVersion(); + val developerEmail = projectProperty.getDeveloperEmail(); + val developerUrl = projectProperty.getDeveloperUrl(); + return new ApiInfoBuilder() + .title(String.format("API for %s@%s", projectArtifactId, version)) + .description(String.format("%s %sArtifact ID: %s%sEnvironment: %s", + projectProperty.getDescription(), + LINE_SEPARATOR, + projectArtifactId, + LINE_SEPARATOR, + projectProperty.getEnvironment())) + .contact(new Contact(String.format("%s, email: %s%sHome page: %s", + projectProperty.getDeveloperName(), + developerEmail, + LINE_SEPARATOR, + developerUrl), + developerUrl, developerEmail)) + .version(version) + .build(); + } } diff --git a/gateway/src/main/java/com/jmsoftware/maf/gateway/universal/configuration/WebFluxSecurityConfiguration.java b/gateway/src/main/java/com/jmsoftware/maf/gateway/universal/configuration/WebFluxSecurityConfiguration.java index 48fb1fa6..e13f77b8 100644 --- a/gateway/src/main/java/com/jmsoftware/maf/gateway/universal/configuration/WebFluxSecurityConfiguration.java +++ b/gateway/src/main/java/com/jmsoftware/maf/gateway/universal/configuration/WebFluxSecurityConfiguration.java @@ -1,18 +1,18 @@ package com.jmsoftware.maf.gateway.universal.configuration; +import com.jmsoftware.maf.gateway.universal.filter.RequestFilter; import lombok.RequiredArgsConstructor; import lombok.extern.slf4j.Slf4j; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; import org.springframework.http.HttpMethod; -import org.springframework.http.HttpStatus; import org.springframework.security.authentication.ReactiveAuthenticationManager; import org.springframework.security.config.annotation.web.reactive.EnableWebFluxSecurity; +import org.springframework.security.config.web.server.SecurityWebFiltersOrder; import org.springframework.security.config.web.server.ServerHttpSecurity; import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder; import org.springframework.security.web.server.SecurityWebFilterChain; import org.springframework.security.web.server.context.ServerSecurityContextRepository; -import reactor.core.publisher.Mono; import java.util.ArrayList; @@ -20,9 +20,12 @@ * Description: WebFluxSecurityConfiguration, change description here. * * @author 钟俊(zhongjun), email: zhongjun@toguide.cn, date: 12/18/2020 3:23 PM - * @see Spring Secirity Reference - Reactive Applications - * @see Securing Spring WebFlux Reactive APIs with JWT Auth - * @see SpringCloud Gateway 整合 Spring Security Webflux 的关键点(痛点解析),及示例项目 + * @see + * Spring Secirity Reference - Reactive Applications + * @see + * Securing Spring WebFlux Reactive APIs with JWT Auth + * @see + * SpringCloud Gateway 整合 Spring Security Webflux 的关键点(痛点解析),及示例项目 **/ @Slf4j @Configuration @@ -32,18 +35,20 @@ public class WebFluxSecurityConfiguration { private final CustomConfiguration customConfiguration; private final ReactiveAuthenticationManager reactiveAuthenticationManager; private final ServerSecurityContextRepository securityContextRepository; + private final ServerAuthenticationEntryPointImpl serverAuthenticationEntryPointImpl; + private final CustomServerAccessDeniedHandler customServerAccessDeniedHandler; + private final RequestFilter requestFilter; @Bean SecurityWebFilterChain springWebFilterChain(ServerHttpSecurity http) { - return http.cors().disable() + return http + .cors().disable() + .csrf().disable() .exceptionHandling() - // TODO: response JSON - .authenticationEntryPoint((serverWebExchange, authenticationException) -> - Mono.fromRunnable(() -> serverWebExchange.getResponse().setStatusCode(HttpStatus.UNAUTHORIZED))) - .accessDeniedHandler((serverWebExchange, accessDeniedException) -> - Mono.fromRunnable(() -> serverWebExchange.getResponse().setStatusCode(HttpStatus.FORBIDDEN))) + .authenticationEntryPoint(serverAuthenticationEntryPointImpl) + .accessDeniedHandler(customServerAccessDeniedHandler) .and() - .csrf().disable() + .addFilterBefore(requestFilter, SecurityWebFiltersOrder.AUTHENTICATION) .authenticationManager(reactiveAuthenticationManager) .securityContextRepository(securityContextRepository) .authorizeExchange() diff --git a/gateway/src/main/java/com/jmsoftware/maf/gateway/universal/filter/AuthGlobalFilter.java b/gateway/src/main/java/com/jmsoftware/maf/gateway/universal/filter/AuthGlobalFilter.java deleted file mode 100644 index 810b5ae5..00000000 --- a/gateway/src/main/java/com/jmsoftware/maf/gateway/universal/filter/AuthGlobalFilter.java +++ /dev/null @@ -1,24 +0,0 @@ -package com.jmsoftware.maf.gateway.universal.filter; - -import org.springframework.cloud.gateway.filter.GatewayFilterChain; -import org.springframework.cloud.gateway.filter.GlobalFilter; -import org.springframework.core.annotation.Order; -import org.springframework.stereotype.Component; -import org.springframework.web.server.ServerWebExchange; -import reactor.core.publisher.Mono; - -/** - * Description: AuthGlobalFilter, change description here. - * - * @author 钟俊(zhongjun), email: zhongjun@toguide.cn, date: 12/18/2020 2:52 PM - * @see - * Global Filters - **/ -@Component -@Order(Integer.MIN_VALUE) -public class AuthGlobalFilter implements GlobalFilter { - @Override - public Mono filter(ServerWebExchange exchange, GatewayFilterChain chain) { - return null; - } -} diff --git a/gateway/src/main/java/com/jmsoftware/maf/gateway/universal/filter/RequestFilter.java b/gateway/src/main/java/com/jmsoftware/maf/gateway/universal/filter/RequestFilter.java index b6a728df..7efe0d3e 100644 --- a/gateway/src/main/java/com/jmsoftware/maf/gateway/universal/filter/RequestFilter.java +++ b/gateway/src/main/java/com/jmsoftware/maf/gateway/universal/filter/RequestFilter.java @@ -1,13 +1,11 @@ package com.jmsoftware.maf.gateway.universal.filter; -import com.jmsoftware.maf.gateway.universal.configuration.ProjectProperty; import com.jmsoftware.maf.gateway.universal.util.RequestUtil; -import lombok.RequiredArgsConstructor; import lombok.extern.slf4j.Slf4j; -import org.springframework.cloud.gateway.filter.GatewayFilterChain; -import org.springframework.cloud.gateway.filter.GlobalFilter; import org.springframework.stereotype.Component; import org.springframework.web.server.ServerWebExchange; +import org.springframework.web.server.WebFilter; +import org.springframework.web.server.WebFilterChain; import reactor.core.publisher.Mono; /** @@ -20,16 +18,20 @@ **/ @Slf4j @Component -@RequiredArgsConstructor -public class RequestFilter implements GlobalFilter { - private final ProjectProperty projectProperty; - +public class RequestFilter implements WebFilter { @Override - public Mono filter(ServerWebExchange exchange, GatewayFilterChain chain) { - log.info("{} intercepted requesters access. Requester: {}, resource: [{}] {}", - projectProperty.getProjectArtifactId().toUpperCase(), + @SuppressWarnings("NullableProblems") + public Mono filter(ServerWebExchange exchange, WebFilterChain chain) { + log.info("{} (pre). Requester: {}, resource: [{}] {}", + this.getClass().getSimpleName(), RequestUtil.getRequestIpAndPort(exchange.getRequest()), exchange.getRequest().getMethod(), exchange.getRequest().getURI()); - return chain.filter(exchange); + return chain.filter(exchange).then( + Mono.fromRunnable(() -> log.info("{} (post). Requester: {}, resource: [{}] {}", + this.getClass().getSimpleName(), + RequestUtil.getRequestIpAndPort(exchange.getRequest()), + exchange.getRequest().getMethod(), + exchange.getRequest().getURI())) + ); } } diff --git a/gateway/src/main/java/com/jmsoftware/maf/gateway/universal/service/impl/CommonServiceImpl.java b/gateway/src/main/java/com/jmsoftware/maf/gateway/universal/service/impl/CommonServiceImpl.java index 5e058ed3..f3fc2f59 100644 --- a/gateway/src/main/java/com/jmsoftware/maf/gateway/universal/service/impl/CommonServiceImpl.java +++ b/gateway/src/main/java/com/jmsoftware/maf/gateway/universal/service/impl/CommonServiceImpl.java @@ -1,6 +1,5 @@ package com.jmsoftware.maf.gateway.universal.service.impl; -import com.jmsoftware.maf.gateway.universal.aspect.ValidateArgument; import com.jmsoftware.maf.gateway.universal.configuration.ProjectProperty; import com.jmsoftware.maf.gateway.universal.domain.ValidationTestPayload; import com.jmsoftware.maf.gateway.universal.service.CommonService; @@ -44,7 +43,6 @@ public Map getApplicationInfo() { } @Override - @ValidateArgument public void validateObject(@Valid ValidationTestPayload payload) { log.info("Validation passed! {}", payload); } diff --git a/gateway/src/main/java/com/jmsoftware/maf/gateway/universal/util/ResponseUtil.java b/gateway/src/main/java/com/jmsoftware/maf/gateway/universal/util/ResponseUtil.java new file mode 100644 index 00000000..fbf7fd05 --- /dev/null +++ b/gateway/src/main/java/com/jmsoftware/maf/gateway/universal/util/ResponseUtil.java @@ -0,0 +1,48 @@ +package com.jmsoftware.maf.gateway.universal.util; + +import cn.hutool.json.JSONObject; +import cn.hutool.json.JSONUtil; +import lombok.NonNull; +import lombok.SneakyThrows; +import lombok.val; +import org.springframework.core.io.buffer.DataBuffer; +import org.springframework.http.HttpStatus; +import org.springframework.lang.Nullable; +import org.springframework.web.server.ServerWebExchange; +import reactor.core.publisher.Mono; + +import java.nio.charset.StandardCharsets; +import java.util.Date; + +/** + * Description: ResponseUtil, change description here. + * + * @author 钟俊(zhongjun), email: zhongjun@toguide.cn, date: 12/21/2020 9:19 AM + **/ +public class ResponseUtil { + /** + * Render json mono. + * + * @param exchange the exchange + * @param httpStatus the http status + * @param data the data + * @return the mono + */ + @SneakyThrows + public static Mono renderJson(@NonNull ServerWebExchange exchange, @NonNull HttpStatus httpStatus, + @Nullable Object data) { + exchange.getResponse().setStatusCode(httpStatus); + val response = exchange.getResponse(); + val responseJson = new JSONObject(); + responseJson.getConfig().setIgnoreNullValue(false).setDateFormat("yyyy-MM-dd HH:mm:ss"); + responseJson.set("timestamp", new Date()) + .set("status", httpStatus.value()) + .set("message", httpStatus.getReasonPhrase()) + .set("data", data); + val responseBodyBytes = JSONUtil.toJsonStr(responseJson).getBytes(StandardCharsets.UTF_8); + DataBuffer dataBuffer = response.bufferFactory().wrap(responseBodyBytes); + response.setStatusCode(httpStatus); + response.getHeaders().add("Content-Type", "application/json;charset=UTF-8"); + return response.writeWith(Mono.just(dataBuffer)); + } +} diff --git a/gateway/src/main/resources/application-development-docker.yml b/gateway/src/main/resources/application-development-docker.yml index 82d85300..4e5a1414 100644 --- a/gateway/src/main/resources/application-development-docker.yml +++ b/gateway/src/main/resources/application-development-docker.yml @@ -11,5 +11,28 @@ spring: custom: configuration: - allowed-application-list: - - api-portal + super-user: "admin" + # Make `web-security-disabled` equal to true to disable web security. We suggest you do not turn off web security + # feature unless development environment. + web-security-disabled: false + # Disable web request information log + web-request-log-disabled: false + ignored-request: + post: + - "/authentication/**" + get: + - "/favicon.ico" + - "/auth/check-username-uniqueness" + - "/auth/check-email-uniqueness" + - "/auth/validate-username/**" + - "/user/get-avatar" + - "/common/get-jwt" + pattern: + - "/static/**" + - "/actuator/**" + - "/druid/**" + - "/swagger-resources/**" + - "/v2/api-docs/**" + - "/**/v2/api-docs/**" + - "/webjars/**" + - "/doc.html" diff --git a/gateway/src/main/resources/application-development-local.yml b/gateway/src/main/resources/application-development-local.yml index 68269d47..b0580dee 100644 --- a/gateway/src/main/resources/application-development-local.yml +++ b/gateway/src/main/resources/application-development-local.yml @@ -23,8 +23,6 @@ spring: custom: configuration: - # Leave `allowed-application-list` empty - allowed-application-list: super-user: "admin" # Make `web-security-disabled` equal to true to disable web security. We suggest you do not turn off web security # feature unless development environment. @@ -35,13 +33,18 @@ custom: post: - "/authentication/**" get: + - "/favicon.ico" - "/auth/check-username-uniqueness" - "/auth/check-email-uniqueness" - "/auth/validate-username/**" - "/user/get-avatar" - "/common/get-jwt" pattern: + - "/static/**" - "/actuator/**" - "/druid/**" - "/swagger-resources/**" - "/v2/api-docs/**" + - "/*/v2/api-docs/**" + - "/webjars/**" + - "/doc.html" diff --git a/gateway/src/main/resources/application-production.yml b/gateway/src/main/resources/application-production.yml index 422008d9..7ca32473 100644 --- a/gateway/src/main/resources/application-production.yml +++ b/gateway/src/main/resources/application-production.yml @@ -11,5 +11,28 @@ spring: custom: configuration: - allowed-application-list: - - api-portal + super-user: "admin" + # Make `web-security-disabled` equal to true to disable web security. We suggest you do not turn off web security + # feature unless development environment. + web-security-disabled: false + # Disable web request information log + web-request-log-disabled: false + ignored-request: + post: + - "/authentication/**" + get: + - "/favicon.ico" + - "/auth/check-username-uniqueness" + - "/auth/check-email-uniqueness" + - "/auth/validate-username/**" + - "/user/get-avatar" + - "/common/get-jwt" + pattern: + - "/static/**" + - "/actuator/**" + - "/druid/**" + - "/swagger-resources/**" + - "/v2/api-docs/**" + - "/**/v2/api-docs/**" + - "/webjars/**" + - "/doc.html" diff --git a/gateway/src/main/resources/application-stage.yml b/gateway/src/main/resources/application-stage.yml index bb94831c..35d9b0f0 100644 --- a/gateway/src/main/resources/application-stage.yml +++ b/gateway/src/main/resources/application-stage.yml @@ -11,5 +11,28 @@ spring: custom: configuration: - allowed-application-list: - - api-portal + super-user: "admin" + # Make `web-security-disabled` equal to true to disable web security. We suggest you do not turn off web security + # feature unless development environment. + web-security-disabled: false + # Disable web request information log + web-request-log-disabled: false + ignored-request: + post: + - "/authentication/**" + get: + - "/favicon.ico" + - "/auth/check-username-uniqueness" + - "/auth/check-email-uniqueness" + - "/auth/validate-username/**" + - "/user/get-avatar" + - "/common/get-jwt" + pattern: + - "/static/**" + - "/actuator/**" + - "/druid/**" + - "/swagger-resources/**" + - "/v2/api-docs/**" + - "/**/v2/api-docs/**" + - "/webjars/**" + - "/doc.html" diff --git a/gateway/src/main/resources/application-test.yml b/gateway/src/main/resources/application-test.yml index 4ba9fb8a..3a4867ad 100644 --- a/gateway/src/main/resources/application-test.yml +++ b/gateway/src/main/resources/application-test.yml @@ -11,5 +11,28 @@ spring: custom: configuration: - allowed-application-list: - - api-portal + super-user: "admin" + # Make `web-security-disabled` equal to true to disable web security. We suggest you do not turn off web security + # feature unless development environment. + web-security-disabled: false + # Disable web request information log + web-request-log-disabled: false + ignored-request: + post: + - "/authentication/**" + get: + - "/favicon.ico" + - "/auth/check-username-uniqueness" + - "/auth/check-email-uniqueness" + - "/auth/validate-username/**" + - "/user/get-avatar" + - "/common/get-jwt" + pattern: + - "/static/**" + - "/actuator/**" + - "/druid/**" + - "/swagger-resources/**" + - "/v2/api-docs/**" + - "/**/v2/api-docs/**" + - "/webjars/**" + - "/doc.html"