commit:集成knife4j

This commit is contained in:
Jerry
2020-10-29 18:51:32 +08:00
parent c87e94d2e8
commit 6e57f10dfc
648 changed files with 6447 additions and 708 deletions

View File

@@ -18,7 +18,7 @@ import org.springframework.web.bind.annotation.RestController;
* 网关服务启动类。
*
* @author Jerry
* @date 2020-10-19
* @date 2020-08-08
*/
@EnableAutoConfiguration(exclude = {DataSourceAutoConfiguration.class})
@SpringCloudApplication

View File

@@ -5,11 +5,13 @@ import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.cloud.context.config.annotation.RefreshScope;
import org.springframework.context.annotation.Configuration;
import java.util.Set;
/**
* 网关业务配置类。
*
* @author Jerry
* @date 2020-10-19
* @date 2020-08-08
*/
@Data
@RefreshScope
@@ -18,7 +20,7 @@ import org.springframework.context.annotation.Configuration;
public class ApplicationConfig {
/**
* token加密时的盐
* token加密用的密钥该值的长度最少10个字符(过短会报错)。
*/
private String tokenSigningKey;
/**
@@ -47,4 +49,12 @@ public class ApplicationConfig {
* 缺省值是 one day
*/
private int permRedisExpiredSeconds = 86400;
/**
* 基于完全等于(equals)判定规则的白名单地址集合过滤效率高于whitelistUrlPattern。
*/
private Set<String> whitelistUrl;
/**
* 基于Ant Pattern模式判定规则的白名单地址集合。如/aa/**。
*/
private Set<String> whitelistUrlPattern;
}

View File

@@ -12,7 +12,7 @@ import org.springframework.web.util.pattern.PathPatternParser;
* 跨域信任配置类。
*
* @author Jerry
* @date 2020-10-19
* @date 2020-08-08
*/
@Configuration
public class CorsConfig {

View File

@@ -11,7 +11,7 @@ import java.nio.charset.StandardCharsets;
* Web通用过滤器配置类。
*
* @author Jerry
* @date 2020-10-19
* @date 2020-08-08
*/
@Configuration
public class FilterConfig {

View File

@@ -17,7 +17,7 @@ import java.util.*;
* Spring Cloud Gateway的Sentinel流控配置类。
*
* @author Jerry
* @date 2020-10-19
* @date 2020-08-08
*/
@Configuration
public class SentinelConfig {

View File

@@ -0,0 +1,52 @@
package com.orange.demo.gateway.config;
import lombok.AllArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.cloud.gateway.config.GatewayProperties;
import org.springframework.cloud.gateway.route.RouteLocator;
import org.springframework.cloud.gateway.support.NameUtils;
import org.springframework.context.annotation.Primary;
import org.springframework.stereotype.Component;
import springfox.documentation.swagger.web.SwaggerResource;
import springfox.documentation.swagger.web.SwaggerResourcesProvider;
import java.util.ArrayList;
import java.util.List;
/***
* 返回Swagger UI需要读取的资源数据这里是微服务的路由数据。
*
* @author Knife4j Team。
* @date 2020-08-08
*/
@Slf4j
@Component
@Primary
@AllArgsConstructor
public class SwaggerResourceConfig implements SwaggerResourcesProvider {
private final RouteLocator routeLocator;
private final GatewayProperties gatewayProperties;
@Override
public List<SwaggerResource> get() {
List<SwaggerResource> resources = new ArrayList<>();
List<String> routes = new ArrayList<>();
routeLocator.getRoutes().subscribe(route -> routes.add(route.getId()));
gatewayProperties.getRoutes().stream().filter(routeDefinition -> routes.contains(routeDefinition.getId()))
.forEach(route -> route.getPredicates().stream()
.filter(predicateDefinition -> ("Path").equalsIgnoreCase(predicateDefinition.getName()))
.forEach(predicateDefinition -> resources.add(swaggerResource(route.getId(),
predicateDefinition.getArgs().get(NameUtils.GENERATED_NAME_PREFIX + "0")
.replace("**", "v2/api-docs")))));
return resources;
}
private SwaggerResource swaggerResource(String name, String location) {
SwaggerResource swaggerResource = new SwaggerResource();
swaggerResource.setName(name);
swaggerResource.setLocation(location);
swaggerResource.setSwaggerVersion("2.0");
return swaggerResource;
}
}

View File

@@ -4,7 +4,7 @@ package com.orange.demo.gateway.constant;
* 网关业务相关的常量对象。
*
* @author Jerry
* @date 2020-10-19
* @date 2020-08-08
*/
public final class GatewayConstant {

View File

@@ -12,7 +12,6 @@ import com.orange.demo.common.core.util.RedisKeyUtil;
import com.orange.demo.gateway.config.ApplicationConfig;
import com.orange.demo.gateway.constant.GatewayConstant;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.collections4.CollectionUtils;
import org.apache.commons.lang3.StringUtils;
import org.reactivestreams.Publisher;
import org.springframework.beans.factory.annotation.Autowired;
@@ -46,7 +45,7 @@ import java.util.Map;
* 全局后处理过滤器。主要用于将用户的会话信息存到缓存服务器,以及在登出时清除缓存中的会话数据。
*
* @author Jerry
* @date 2020-10-19
* @date 2020-08-08
*/
@Slf4j
public class AuthenticationPostFilter implements GlobalFilter, Ordered {
@@ -235,21 +234,12 @@ public class AuthenticationPostFilter implements GlobalFilter, Ordered {
}
t.exec();
}
// 4. 构造返回给用户的应答
JSONObject resultJsonData = new JSONObject();
resultJsonData.put(TokenData.REQUEST_ATTRIBUTE_NAME, token);
resultJsonData.put("isAdmin", isAdmin);
resultJsonData.put("showName", showName);
JSONArray menuList = loginData.getJSONArray("menuList");
if (CollectionUtils.isNotEmpty(menuList)) {
resultJsonData.put("menuList", menuList);
// 4. 构造返回给用户的应答,将加密后的令牌返回给前端。
loginData.put(TokenData.REQUEST_ATTRIBUTE_NAME, token);
// 如果是管理员,不用返回权限字列表。
if (Boolean.TRUE.equals(isAdmin)) {
loginData.remove("permCodeList");
}
if (Boolean.FALSE.equals(isAdmin)) {
JSONArray permCodeList = loginData.getJSONArray("permCodeList");
if (CollectionUtils.isNotEmpty(permCodeList)) {
resultJsonData.put("permCodeList", permCodeList);
}
}
return ResponseResult.success(resultJsonData);
return ResponseResult.success(loginData);
}
}

View File

@@ -12,6 +12,7 @@ import com.orange.demo.gateway.config.ApplicationConfig;
import com.orange.demo.gateway.constant.GatewayConstant;
import io.jsonwebtoken.Claims;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.collections4.CollectionUtils;
import org.apache.commons.lang3.StringUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.cloud.gateway.filter.GatewayFilterChain;
@@ -21,6 +22,7 @@ import org.springframework.http.HttpStatus;
import org.springframework.http.MediaType;
import org.springframework.http.server.reactive.ServerHttpRequest;
import org.springframework.http.server.reactive.ServerHttpResponse;
import org.springframework.util.AntPathMatcher;
import org.springframework.web.server.ServerWebExchange;
import reactor.core.publisher.Flux;
import reactor.core.publisher.Mono;
@@ -30,15 +32,13 @@ import redis.clients.jedis.JedisPool;
import java.io.UnsupportedEncodingException;
import java.net.URLEncoder;
import java.nio.charset.StandardCharsets;
import java.util.LinkedList;
import java.util.List;
import java.util.Map;
/**
* 全局前处理过滤器。主要用于用户操作权限验证。
*
* @author Jerry
* @date 2020-10-19
* @date 2020-08-08
*/
@Slf4j
public class AuthenticationPreFilter implements GlobalFilter, Ordered {
@@ -47,20 +47,18 @@ public class AuthenticationPreFilter implements GlobalFilter, Ordered {
private ApplicationConfig appConfig;
@Autowired
private JedisPool jedisPool;
private static List<String> whitelistUrlPattern = new LinkedList<>();
static {
// 这里可以添加URL部分匹配的白名单列表
// 另外解释一下数据库中配置的白名单列表在doLogin中直接合并到当前用户的权限列表中了。
}
/**
* Ant Pattern模式的白名单地址匹配器。
*/
private AntPathMatcher antMatcher = new AntPathMatcher();
@Override
public Mono<Void> filter(ServerWebExchange exchange, GatewayFilterChain chain) {
ServerHttpRequest request = exchange.getRequest();
ServerHttpResponse response = exchange.getResponse();
String url = request.getURI().getPath();
// 登录请求直接转发给login验证服务器
if (url.equals(GatewayConstant.ADMIN_LOGIN_URL)) {
// 判断是否为白名单请求,以及一些内置不需要验证的请求。(登录请求也包含其中)
if (this.shouldNotFilter(url)) {
return chain.filter(exchange);
}
String token = this.getTokenFromRequest(request);
@@ -155,15 +153,37 @@ public class AuthenticationPreFilter implements GlobalFilter, Ordered {
private boolean hasPermission(Jedis jedis, String sessionId, String url) {
// 对于退出登录操作,不需要进行权限验证,仅仅确认是已经登录的合法用户即可。
if (url.equals(GatewayConstant.ADMIN_LOGOUT_URL)
|| Boolean.TRUE.equals(jedis.sismember(RedisKeyUtil.makeSessionPermIdKeyForRedis(sessionId), url))) {
return url.equals(GatewayConstant.ADMIN_LOGOUT_URL)
|| Boolean.TRUE.equals(jedis.sismember(RedisKeyUtil.makeSessionPermIdKeyForRedis(sessionId), url));
}
/**
* 判断当前请求的url是否为配置中的白名单地址。以及一些内置的不需要登录即可访问的url。
* @param url 请求的url。
* @return 是返回true否则false。
*/
private boolean shouldNotFilter(String url) {
// 这里过滤和swagger相关的url
if (url.endsWith("/v2/api-docs") || url.endsWith("/v2/api-docs-ext")) {
return true;
}
for (String urlPattern : whitelistUrlPattern) {
if (url.startsWith(urlPattern)) {
if (url.equals(GatewayConstant.ADMIN_LOGIN_URL)) {
return true;
}
// 先过滤直接匹配的白名单url。
if (CollectionUtils.isNotEmpty(appConfig.getWhitelistUrl())) {
if (appConfig.getWhitelistUrl().contains(url)) {
return true;
}
}
// 过滤ant pattern模式的白名单url。
if (CollectionUtils.isNotEmpty(appConfig.getWhitelistUrlPattern())) {
for (String urlPattern : appConfig.getWhitelistUrlPattern()) {
if (antMatcher.match(urlPattern, url)) {
return true;
}
}
}
return false;
}
}

View File

@@ -17,7 +17,7 @@ import reactor.core.publisher.Mono;
* 为整个链路生成唯一的traceId并存储在Request Head中。
*
* @author Jerry
* @date 2020-10-19
* @date 2020-08-08
*/
@Slf4j
public class RequestLogFilter implements GlobalFilter, Ordered {

View File

@@ -16,7 +16,7 @@ import reactor.core.publisher.Mono;
* 将整个链路的traceId存储在Response Head中并返回给前端便于问题定位。
*
* @author Jerry
* @date 2020-10-19
* @date 2020-08-08
*/
@Slf4j
public class ResponseLogFilter implements GlobalFilter, Ordered {

View File

@@ -0,0 +1,53 @@
package com.orange.demo.gateway.handler;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.http.HttpStatus;
import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RestController;
import reactor.core.publisher.Mono;
import springfox.documentation.swagger.web.*;
import java.util.Optional;
/**
* Swagger的资源请求处理器。
*
* @author Knife4j Team。
* @date 2020-08-08
*/
@RestController
public class SwaggerHandler {
@Autowired(required = false)
private SecurityConfiguration securityConfiguration;
@Autowired(required = false)
private UiConfiguration uiConfiguration;
private final SwaggerResourcesProvider swaggerResources;
@Autowired
public SwaggerHandler(SwaggerResourcesProvider swaggerResources) {
this.swaggerResources = swaggerResources;
}
@GetMapping("/swagger-resources/configuration/security")
public Mono<ResponseEntity<SecurityConfiguration>> securityConfiguration() {
return Mono.just(new ResponseEntity<>(
Optional.ofNullable(securityConfiguration)
.orElse(SecurityConfigurationBuilder.builder().build()), HttpStatus.OK));
}
@GetMapping("/swagger-resources/configuration/ui")
public Mono<ResponseEntity<UiConfiguration>> uiConfiguration() {
return Mono.just(new ResponseEntity<>(
Optional.ofNullable(uiConfiguration)
.orElse(UiConfigurationBuilder.builder().build()), HttpStatus.OK));
}
@GetMapping("/swagger-resources")
public Mono<ResponseEntity> swaggerResources() {
return Mono.just((new ResponseEntity<>(swaggerResources.get(), HttpStatus.OK)));
}
}

View File

@@ -59,6 +59,9 @@
<Root level="${OUTPUT_LOG_LEVEL}">
<AppenderRef ref="console"/>
</Root>
<Logger name="springfox.documentation" additivity="false" level="error">
<AppenderRef ref="console"/>
</Logger>
<!-- AsyncLogger 是基于Disruptor的全量异步队列性能极高队列默认大小4096。-->
<!-- 队列默认值可通过JVM参数设置参考博客https://www.jianshu.com/p/82469047acbf -->
<AsyncLogger name="com.orange.demo" additivity="false" level="info">