Skip to content

Commit

Permalink
feat($gateway): make Swagger and Spring Security work together
Browse files Browse the repository at this point in the history
  • Loading branch information
Johnny Miller (锺俊) committed Dec 21, 2020
1 parent efb83ad commit c2c4721
Show file tree
Hide file tree
Showing 16 changed files with 285 additions and 266 deletions.

This file was deleted.

This file was deleted.

Original file line number Diff line number Diff line change
@@ -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;

/**
* <h1>CustomConfiguration</h1>
Expand All @@ -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<String> allowedApplicationList = Lists.newLinkedList();
/**
* <p>The username of super user who has no restriction to access any system&#39;s resources.</p>
* <p><strong>ATTENTION</strong>: The value of username of super user must be equal to the value that is
* persistent in database.</p>
*/
@NotBlank
private String superUser;
/**
* Ignore URLs
Expand Down
Original file line number Diff line number Diff line change
@@ -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<Void> 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);
}
}
Original file line number Diff line number Diff line change
@@ -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<Void> 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);
}
}
Original file line number Diff line number Diff line change
@@ -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;

Expand All @@ -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.
Expand All @@ -53,26 +57,48 @@ public List<SwaggerResource> get() {
val swaggerResourceList = new LinkedList<SwaggerResource>();
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();
}
}
Loading

0 comments on commit c2c4721

Please sign in to comment.