mirror of
https://gitee.com/orangeform/orange-admin.git
synced 2026-01-18 02:56:30 +08:00
commit:集成knife4j
This commit is contained in:
@@ -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
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
@@ -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 {
|
||||
|
||||
@@ -11,7 +11,7 @@ import java.nio.charset.StandardCharsets;
|
||||
* Web通用过滤器配置类。
|
||||
*
|
||||
* @author Jerry
|
||||
* @date 2020-10-19
|
||||
* @date 2020-08-08
|
||||
*/
|
||||
@Configuration
|
||||
public class FilterConfig {
|
||||
|
||||
@@ -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 {
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
@@ -4,7 +4,7 @@ package com.orange.demo.gateway.constant;
|
||||
* 网关业务相关的常量对象。
|
||||
*
|
||||
* @author Jerry
|
||||
* @date 2020-10-19
|
||||
* @date 2020-08-08
|
||||
*/
|
||||
public final class GatewayConstant {
|
||||
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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 {
|
||||
|
||||
@@ -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 {
|
||||
|
||||
@@ -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)));
|
||||
}
|
||||
}
|
||||
@@ -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">
|
||||
|
||||
Reference in New Issue
Block a user