fix(*) 首次提交General项目
This commit is contained in:
114
Gateway/pom.xml
Normal file
114
Gateway/pom.xml
Normal file
@ -0,0 +1,114 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<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>cn.crtech.cloud.gateway</groupId>
|
||||
<artifactId>Gateway</artifactId>
|
||||
<version>1.0.1</version>
|
||||
|
||||
<!-- 父工程 -->
|
||||
<parent>
|
||||
<groupId>cn.crtech.cloud.dependencies</groupId>
|
||||
<artifactId>Dependencies</artifactId>
|
||||
<version>1.0.1</version>
|
||||
<relativePath/>
|
||||
</parent>
|
||||
|
||||
<!-- 依赖的版本锁定 -->
|
||||
<properties>
|
||||
<maven.compiler.source>8</maven.compiler.source>
|
||||
<maven.compiler.target>8</maven.compiler.target>
|
||||
<common.version>1.0.1</common.version>
|
||||
</properties>
|
||||
|
||||
<dependencies>
|
||||
<dependency>
|
||||
<groupId>cn.crtech.cloud.common</groupId>
|
||||
<artifactId>Common</artifactId>
|
||||
<version>${common.version}</version>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
<groupId>org.springframework.cloud</groupId>
|
||||
<artifactId>spring-cloud-starter-gateway</artifactId>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
<groupId>org.apache.commons</groupId>
|
||||
<artifactId>commons-lang3</artifactId>
|
||||
</dependency>
|
||||
|
||||
<!-- nacos 客户端-->
|
||||
<dependency>
|
||||
<groupId>com.alibaba.cloud</groupId>
|
||||
<artifactId>spring-cloud-starter-alibaba-nacos-discovery</artifactId>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
<groupId>org.springframework.security</groupId>
|
||||
<artifactId>spring-security-config</artifactId>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
<groupId>org.springframework.security</groupId>
|
||||
<artifactId>spring-security-oauth2-resource-server</artifactId>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
<groupId>org.springframework.security</groupId>
|
||||
<artifactId>spring-security-oauth2-client</artifactId>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
<groupId>org.springframework.security</groupId>
|
||||
<artifactId>spring-security-oauth2-jose</artifactId>
|
||||
</dependency>
|
||||
|
||||
<!-- 自定义的元数据依赖 -->
|
||||
<dependency>
|
||||
<groupId>org.springframework.boot</groupId>
|
||||
<artifactId>spring-boot-configuration-processor</artifactId>
|
||||
<optional>true</optional>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
<groupId>org.springframework.cloud</groupId>
|
||||
<artifactId>spring-cloud-starter-loadbalancer</artifactId>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
<groupId>org.springframework.cloud</groupId>
|
||||
<artifactId>spring-cloud-starter-openfeign</artifactId>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
<groupId>org.springframework.boot</groupId>
|
||||
<artifactId>spring-boot-starter-data-redis</artifactId>
|
||||
<exclusions>
|
||||
<exclusion>
|
||||
<groupId>io.lettuce</groupId>
|
||||
<artifactId>lettuce-core</artifactId>
|
||||
</exclusion>
|
||||
</exclusions>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
<groupId>redis.clients</groupId>
|
||||
<artifactId>jedis</artifactId>
|
||||
</dependency>
|
||||
</dependencies>
|
||||
|
||||
<build>
|
||||
<plugins>
|
||||
<plugin>
|
||||
<groupId>org.springframework.boot</groupId>
|
||||
<artifactId>spring-boot-maven-plugin</artifactId>
|
||||
<configuration>
|
||||
<jvmArguments>-Dfile.encoding=UTF-8</jvmArguments>
|
||||
</configuration>
|
||||
</plugin>
|
||||
</plugins>
|
||||
</build>
|
||||
</project>
|
||||
@ -0,0 +1,20 @@
|
||||
package cn.crtech.cloud.gateway;
|
||||
|
||||
import org.springframework.boot.SpringApplication;
|
||||
import org.springframework.boot.autoconfigure.SpringBootApplication;
|
||||
import org.springframework.boot.autoconfigure.jdbc.DataSourceAutoConfiguration;
|
||||
import org.springframework.cloud.client.discovery.EnableDiscoveryClient;
|
||||
|
||||
/**
|
||||
* Author : yj
|
||||
* Date : 2020-12-31
|
||||
* Description:
|
||||
*/
|
||||
|
||||
@EnableDiscoveryClient
|
||||
@SpringBootApplication(exclude = {DataSourceAutoConfiguration.class})
|
||||
public class GatewayApplication {
|
||||
public static void main(String[] args) {
|
||||
SpringApplication.run(GatewayApplication.class);
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,282 @@
|
||||
package cn.crtech.cloud.gateway.authorization;
|
||||
|
||||
import cn.crtech.cloud.common.constant.AuthConstant;
|
||||
import cn.crtech.cloud.common.constant.RedisConstant;
|
||||
import cn.crtech.cloud.gateway.dto.MisAppDto;
|
||||
import cn.crtech.cloud.gateway.dto.MisUserInfoDto;
|
||||
import cn.crtech.cloud.gateway.filter.AuthGlobalFilter;
|
||||
import cn.hutool.core.util.ObjectUtil;
|
||||
import com.alibaba.fastjson.JSONObject;
|
||||
import com.nimbusds.jose.JWSObject;
|
||||
import org.apache.commons.lang3.ObjectUtils;
|
||||
import org.apache.commons.lang3.StringUtils;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.data.redis.core.RedisTemplate;
|
||||
import org.springframework.http.server.reactive.ServerHttpRequest;
|
||||
import org.springframework.security.authorization.AuthorizationDecision;
|
||||
import org.springframework.security.authorization.ReactiveAuthorizationManager;
|
||||
import org.springframework.security.core.Authentication;
|
||||
import org.springframework.security.core.GrantedAuthority;
|
||||
import org.springframework.security.web.server.authorization.AuthorizationContext;
|
||||
import org.springframework.stereotype.Component;
|
||||
import org.springframework.util.CollectionUtils;
|
||||
import reactor.core.publisher.Mono;
|
||||
|
||||
import java.net.URI;
|
||||
import java.text.ParseException;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
/**
|
||||
* 自定义的鉴权管理器,用于判断是否有资源的访问权限
|
||||
*/
|
||||
|
||||
@Component
|
||||
public class AuthorizationManager implements ReactiveAuthorizationManager<AuthorizationContext> {
|
||||
private RedisTemplate<String, Object> redisTemplate;
|
||||
|
||||
@Autowired
|
||||
private void setRedisTemplate(RedisTemplate<String, Object> redisTemplate) {
|
||||
this.redisTemplate = redisTemplate;
|
||||
}
|
||||
|
||||
private static Logger LOGGER = LoggerFactory.getLogger(AuthGlobalFilter.class);
|
||||
|
||||
@Override
|
||||
public Mono<AuthorizationDecision> check(Mono<Authentication> authentication, AuthorizationContext authorizationContext) {
|
||||
//获取请求URI
|
||||
URI uri = authorizationContext.getExchange().getRequest().getURI();
|
||||
String realPath = uri.getPath();
|
||||
//获取当前登录的用户信息
|
||||
ServerHttpRequest request = authorizationContext.getExchange().getRequest();
|
||||
String token = request.getHeaders().getFirst("Authorization");
|
||||
List<String> userAuthorities = new ArrayList<>();
|
||||
if (token != null) {
|
||||
try {
|
||||
String realToken = token.replace("Bearer ", "");
|
||||
JWSObject jwsObject = JWSObject.parse(realToken);
|
||||
String userStr = jwsObject.getPayload().toString();
|
||||
JSONObject userData = JSONObject.parseObject(userStr);
|
||||
|
||||
Boolean isCompanyAdmin = userData.getBoolean("company_admin");
|
||||
List<String> authorities = JSONObject.parseArray(userData.getJSONArray("authorities").toString(), String.class);
|
||||
String clientId = getAppClientId(realPath);
|
||||
String requestURI = getRealPath(realPath, clientId);
|
||||
Integer userId = userData.getInteger("id");
|
||||
MisAppDto thisApp = getThisApp(userId, clientId);
|
||||
|
||||
// 获取免费授权权限数据
|
||||
userAuthorities = getFreeAuthority(thisApp, clientId, requestURI, authorities);
|
||||
|
||||
// 获取服务间调用免费授权权限
|
||||
userAuthorities.addAll(getAppFeign(requestURI, authorities));
|
||||
|
||||
// 其他相关权限校验处理逻辑
|
||||
if (isCompanyAdmin) {
|
||||
// 获取企业管理员权限
|
||||
userAuthorities.addAll(getAuthorityData(thisApp, RedisConstant.RESOURCE_ADMIN_MAP, requestURI, authorities));
|
||||
} else {
|
||||
// 获取企业角色权限
|
||||
userAuthorities.addAll(getAuthorityData(thisApp, RedisConstant.RESOURCE_ADMIN_MAP, requestURI, authorities));
|
||||
// 获取产品默认角色权限
|
||||
userAuthorities.addAll(getAuthorityData(thisApp, RedisConstant.DEFAULT_ROLES_MAP, requestURI, authorities));
|
||||
}
|
||||
|
||||
//所有的角色前面增加 “ROLE_”
|
||||
userAuthorities = userAuthorities.stream()
|
||||
.map(i -> i = AuthConstant.AUTHORITY_PREFIX + i)
|
||||
.collect(Collectors.toList());
|
||||
//认证通过且角色匹配的用户可访问当前路径
|
||||
LOGGER.info("AuthorizationManager.check() authorities:{}", userAuthorities);
|
||||
} catch (ParseException e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
}
|
||||
|
||||
//双冒号方法引用
|
||||
return authentication
|
||||
.filter(Authentication::isAuthenticated)
|
||||
.flatMapIterable(Authentication::getAuthorities)
|
||||
.map(GrantedAuthority::getAuthority)
|
||||
.any(userAuthorities::contains)
|
||||
.map(AuthorizationDecision::new)
|
||||
.defaultIfEmpty(new AuthorizationDecision(false));
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取当前请求所在产品信息
|
||||
*
|
||||
* @param userId 用户ID
|
||||
* @param clientId 产品标识
|
||||
* @return 返回结果
|
||||
*/
|
||||
private MisAppDto getThisApp(Integer userId, String clientId) {
|
||||
// 获取缓存用户登录授权信息
|
||||
Object userInfoObj = redisTemplate.opsForValue().get(RedisConstant.CURRENT_USREINFO + userId);
|
||||
if (ObjectUtils.isEmpty(userInfoObj)) {
|
||||
return null;
|
||||
}
|
||||
MisUserInfoDto userInfo = JSONObject.parseObject(userInfoObj.toString(), MisUserInfoDto.class);
|
||||
|
||||
// 获取当前授权产品详细数据
|
||||
if (CollectionUtils.isEmpty(userInfo.getApplications())) {
|
||||
return null;
|
||||
}
|
||||
List<MisAppDto> freeAppList = userInfo.getApplications().stream()
|
||||
.filter(item -> item.getCode().equals(clientId))
|
||||
.collect(Collectors.toList());
|
||||
if (CollectionUtils.isEmpty(freeAppList)) {
|
||||
return null;
|
||||
}
|
||||
return freeAppList.get(0);
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取登录用户授权产品免费过滤权限内容
|
||||
*
|
||||
* @param thisApp 产品信息对象
|
||||
* @param requestURI 接口请求地址
|
||||
* @param authorities 已有角色列表
|
||||
* @return 返回处理结果
|
||||
*/
|
||||
public List<String> getFreeAuthority(MisAppDto thisApp, String clientId, String requestURI, List<String> authorities) {
|
||||
if (ObjectUtils.isEmpty(thisApp)) {
|
||||
return new ArrayList<>();
|
||||
}
|
||||
|
||||
// 缓存获取对应免费授权数据
|
||||
String freeAppAuthorityName = RedisConstant.FREE_VERSION_API
|
||||
.replace("${SERIESCODE}", thisApp.getSeriesCode())
|
||||
.replace("${APPCODE}", clientId)
|
||||
.replace("${VERSION}", thisApp.getVersion());
|
||||
|
||||
// 处理请求路由(去除前缀及端口)
|
||||
String realPath = getRealPath(requestURI, clientId);
|
||||
|
||||
return getAuthorityData(thisApp, freeAppAuthorityName, realPath, authorities);
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取服务间调用免费授权权限
|
||||
*
|
||||
* @param realPath 实际请求路由
|
||||
* @param authorities 已有角色数据列表
|
||||
* @return 返回校验处理结果
|
||||
*/
|
||||
public List<String> getAppFeign(String realPath, List<String> authorities) {
|
||||
List<String> userAuthorities = new ArrayList<>();
|
||||
Object authorityObj = redisTemplate.opsForHash().get(RedisConstant.APP_FEIGN_API_MAP, realPath);
|
||||
|
||||
// 如果全部url没有设置权限,就看有没有带*的权限
|
||||
if (ObjectUtil.isEmpty(authorityObj)) {
|
||||
String paths = realPath;
|
||||
boolean first = true;
|
||||
while (paths.indexOf('/') >= 0 && ObjectUtil.isEmpty(authorityObj)) {
|
||||
paths = realPath.substring(0, paths.lastIndexOf("/"));
|
||||
if (first) {
|
||||
first = false;
|
||||
authorityObj = redisTemplate.opsForHash().get(RedisConstant.APP_FEIGN_API_MAP, paths + "/*");
|
||||
if (!ObjectUtil.isEmpty(authorityObj)) break;
|
||||
}
|
||||
authorityObj = redisTemplate.opsForHash().get(RedisConstant.APP_FEIGN_API_MAP, paths + "/**");
|
||||
}
|
||||
}
|
||||
|
||||
// 处理可请求的权限
|
||||
if (!ObjectUtil.isEmpty(authorityObj)) {
|
||||
assert authorityObj != null;
|
||||
String[] requestAuths = authorityObj.toString().split(",");
|
||||
for (String requestAuth : requestAuths) {
|
||||
for (String userAuth : authorities) {
|
||||
if (requestAuth.equals(userAuth)) {
|
||||
userAuthorities.add(userAuth);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
return userAuthorities;
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取用户权限内容并进行校验处理
|
||||
*
|
||||
* @param thisApp 当前请求产品信息
|
||||
* @param redisName 缓存名称
|
||||
* @param realPath 实际请求路由
|
||||
* @param authorities 已有角色数据列表
|
||||
* @return 返回校验处理结果
|
||||
*/
|
||||
public List<String> getAuthorityData(MisAppDto thisApp, String redisName, String realPath, List<String> authorities) {
|
||||
List<String> userAuthorities = new ArrayList<>();
|
||||
Object authorityObj = redisTemplate.opsForHash().get(redisName, realPath);
|
||||
|
||||
// 查询缓存判断此请求是否为特殊权限请求 如果是则进行下一步处理 否则进行下方内容处理
|
||||
boolean isLimitApi = false;
|
||||
if (ObjectUtils.isNotEmpty(thisApp)) {
|
||||
String valueName = thisApp.getSeriesCode() + "_" + thisApp.getCode();
|
||||
Object limitObj = redisTemplate.opsForHash().get(RedisConstant.LIMIT_AUTHORITY_API_MAP, realPath);
|
||||
isLimitApi = (ObjectUtils.isNotEmpty(limitObj) && valueName.equals(limitObj.toString()));
|
||||
}
|
||||
|
||||
// 如果全部url没有设置权限,就看有没有带*的权限
|
||||
if (ObjectUtil.isEmpty(authorityObj) && !isLimitApi) {
|
||||
String paths = realPath;
|
||||
boolean first = true;
|
||||
while (paths.indexOf('/') >= 0 && ObjectUtil.isEmpty(authorityObj)) {
|
||||
paths = realPath.substring(0, paths.lastIndexOf("/"));
|
||||
if (first) {
|
||||
first = false;
|
||||
authorityObj = redisTemplate.opsForHash().get(redisName, paths + "/*");
|
||||
if (!ObjectUtil.isEmpty(authorityObj)) break;
|
||||
}
|
||||
authorityObj = redisTemplate.opsForHash().get(redisName, paths + "/**");
|
||||
}
|
||||
}
|
||||
|
||||
LOGGER.info("可请求的权限 ===> " + authorityObj);
|
||||
LOGGER.info("当前用户权限 ===> " + authorities);
|
||||
|
||||
// 处理可请求的权限
|
||||
if (!ObjectUtil.isEmpty(authorityObj)) {
|
||||
assert authorityObj != null;
|
||||
String[] requestAuths = authorityObj.toString().split(",");
|
||||
for (String requestAuth : requestAuths) {
|
||||
for (String userAuth : authorities) {
|
||||
if (requestAuth.equals(userAuth)) {
|
||||
userAuthorities.add(userAuth);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
return userAuthorities;
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取请求实际有效地址
|
||||
*
|
||||
* @param requestURI 请求地址
|
||||
* @param clientId 产品标识
|
||||
* @return 返回处理结果
|
||||
*/
|
||||
public String getRealPath(String requestURI, String clientId) {
|
||||
if (StringUtils.isAnyBlank(requestURI, clientId)) {
|
||||
throw new NullPointerException("请求地址或产品标识为空");
|
||||
}
|
||||
int splitIndex = requestURI.indexOf("/" + clientId);
|
||||
return splitIndex > -1 ? requestURI.substring(splitIndex) : requestURI;
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取发起请求实际产品标识
|
||||
*
|
||||
* @param requestURI 请求API
|
||||
* @return 返回处理结果
|
||||
*/
|
||||
public String getAppClientId(String requestURI) {
|
||||
String[] splitList = requestURI.split("\\/");
|
||||
return splitList.length > 0 ? splitList[1] : "";
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,33 @@
|
||||
package cn.crtech.cloud.gateway.component;
|
||||
|
||||
import cn.crtech.cloud.common.api.CommonResult;
|
||||
import cn.hutool.json.JSONUtil;
|
||||
import org.springframework.core.io.buffer.DataBuffer;
|
||||
import org.springframework.http.HttpHeaders;
|
||||
import org.springframework.http.HttpStatus;
|
||||
import org.springframework.http.MediaType;
|
||||
import org.springframework.http.server.reactive.ServerHttpResponse;
|
||||
import org.springframework.security.core.AuthenticationException;
|
||||
import org.springframework.security.web.server.ServerAuthenticationEntryPoint;
|
||||
import org.springframework.stereotype.Component;
|
||||
import org.springframework.web.server.ServerWebExchange;
|
||||
import reactor.core.publisher.Mono;
|
||||
|
||||
import java.nio.charset.Charset;
|
||||
|
||||
/**
|
||||
* 自定义返回结果:没有登录或token过期时
|
||||
*/
|
||||
|
||||
@Component
|
||||
public class RestAuthenticationEntryPoint implements ServerAuthenticationEntryPoint {
|
||||
@Override
|
||||
public Mono<Void> commence(ServerWebExchange exchange, AuthenticationException e) {
|
||||
ServerHttpResponse response = exchange.getResponse();
|
||||
response.setStatusCode(HttpStatus.OK);
|
||||
response.getHeaders().add(HttpHeaders.CONTENT_TYPE, MediaType.APPLICATION_JSON_VALUE);
|
||||
String body = JSONUtil.toJsonStr(CommonResult.unauthorized(e.getMessage()));
|
||||
DataBuffer buffer = response.bufferFactory().wrap(body.getBytes(Charset.forName("UTF-8")));
|
||||
return response.writeWith(Mono.just(buffer));
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,33 @@
|
||||
package cn.crtech.cloud.gateway.component;
|
||||
|
||||
import cn.crtech.cloud.common.api.CommonResult;
|
||||
import cn.hutool.json.JSONUtil;
|
||||
import org.springframework.core.io.buffer.DataBuffer;
|
||||
import org.springframework.http.HttpHeaders;
|
||||
import org.springframework.http.HttpStatus;
|
||||
import org.springframework.http.MediaType;
|
||||
import org.springframework.http.server.reactive.ServerHttpResponse;
|
||||
import org.springframework.security.access.AccessDeniedException;
|
||||
import org.springframework.security.web.server.authorization.ServerAccessDeniedHandler;
|
||||
import org.springframework.stereotype.Component;
|
||||
import org.springframework.web.server.ServerWebExchange;
|
||||
import reactor.core.publisher.Mono;
|
||||
|
||||
import java.nio.charset.StandardCharsets;
|
||||
|
||||
/**
|
||||
* 自定义返回结果:没有权限访问时
|
||||
*/
|
||||
|
||||
@Component
|
||||
public class RestfulAccessDeniedHandler implements ServerAccessDeniedHandler {
|
||||
@Override
|
||||
public Mono<Void> handle(ServerWebExchange exchange, AccessDeniedException denied) {
|
||||
ServerHttpResponse response = exchange.getResponse();
|
||||
response.setStatusCode(HttpStatus.OK);
|
||||
response.getHeaders().add(HttpHeaders.CONTENT_TYPE, MediaType.APPLICATION_JSON_VALUE);
|
||||
String body = JSONUtil.toJsonStr(CommonResult.forbidden(denied.getMessage()));
|
||||
DataBuffer buffer = response.bufferFactory().wrap(body.getBytes(StandardCharsets.UTF_8));
|
||||
return response.writeWith(Mono.just(buffer));
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,31 @@
|
||||
package cn.crtech.cloud.gateway.config;
|
||||
|
||||
import org.springframework.boot.context.properties.EnableConfigurationProperties;
|
||||
import org.springframework.cloud.context.config.annotation.RefreshScope;
|
||||
import org.springframework.cloud.gateway.config.GlobalCorsProperties;
|
||||
import org.springframework.context.annotation.Bean;
|
||||
import org.springframework.context.annotation.Configuration;
|
||||
import org.springframework.core.Ordered;
|
||||
import org.springframework.core.annotation.Order;
|
||||
import org.springframework.web.cors.reactive.CorsWebFilter;
|
||||
import org.springframework.web.cors.reactive.UrlBasedCorsConfigurationSource;
|
||||
import org.springframework.web.util.pattern.PathPatternParser;
|
||||
|
||||
/**
|
||||
* Author : yj
|
||||
* Date : 2021-03-12
|
||||
* Description:配置跨域
|
||||
*/
|
||||
|
||||
@Configuration
|
||||
@EnableConfigurationProperties(GlobalCorsProperties.class)
|
||||
public class CorsConfig {
|
||||
@Bean
|
||||
@RefreshScope
|
||||
@Order(Ordered.HIGHEST_PRECEDENCE)
|
||||
public CorsWebFilter corsWebFilter(GlobalCorsProperties globalCorsProperties) {
|
||||
UrlBasedCorsConfigurationSource source = new UrlBasedCorsConfigurationSource(new PathPatternParser());
|
||||
globalCorsProperties.getCorsConfigurations().forEach((k, v) -> source.registerCorsConfiguration(k, v));
|
||||
return new CorsWebFilter(source);
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,19 @@
|
||||
package cn.crtech.cloud.gateway.config;
|
||||
|
||||
import lombok.Data;
|
||||
import lombok.EqualsAndHashCode;
|
||||
import org.springframework.boot.context.properties.ConfigurationProperties;
|
||||
import org.springframework.stereotype.Component;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* 网关白名单配置
|
||||
*/
|
||||
@Data
|
||||
@EqualsAndHashCode(callSuper = false)
|
||||
@Component
|
||||
@ConfigurationProperties(prefix="secure.ignore")
|
||||
public class IgnoreUrlsConfig {
|
||||
private List<String> urls;
|
||||
}
|
||||
@ -0,0 +1,31 @@
|
||||
package cn.crtech.cloud.gateway.config;
|
||||
|
||||
import org.springframework.context.annotation.Bean;
|
||||
import org.springframework.context.annotation.Configuration;
|
||||
import org.springframework.data.redis.connection.RedisConnectionFactory;
|
||||
import org.springframework.data.redis.core.RedisTemplate;
|
||||
import org.springframework.data.redis.repository.configuration.EnableRedisRepositories;
|
||||
import org.springframework.data.redis.serializer.Jackson2JsonRedisSerializer;
|
||||
import org.springframework.data.redis.serializer.StringRedisSerializer;
|
||||
|
||||
/**
|
||||
* Redis相关配置
|
||||
*/
|
||||
|
||||
@Configuration
|
||||
@EnableRedisRepositories
|
||||
public class RedisRepositoryConfig {
|
||||
@Bean
|
||||
public RedisTemplate<String, Object> redisTemplate(RedisConnectionFactory connectionFactory) {
|
||||
RedisTemplate<String, Object> redisTemplate = new RedisTemplate<>();
|
||||
redisTemplate.setConnectionFactory(connectionFactory);
|
||||
StringRedisSerializer stringRedisSerializer = new StringRedisSerializer();
|
||||
redisTemplate.setKeySerializer(stringRedisSerializer);
|
||||
redisTemplate.setHashKeySerializer(stringRedisSerializer);
|
||||
Jackson2JsonRedisSerializer<?> jackson2JsonRedisSerializer = new Jackson2JsonRedisSerializer<>(Object.class);
|
||||
redisTemplate.setValueSerializer(jackson2JsonRedisSerializer);
|
||||
redisTemplate.setHashValueSerializer(jackson2JsonRedisSerializer);
|
||||
redisTemplate.afterPropertiesSet();
|
||||
return redisTemplate;
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,66 @@
|
||||
package cn.crtech.cloud.gateway.config;
|
||||
|
||||
import cn.crtech.cloud.common.constant.AuthConstant;
|
||||
import cn.crtech.cloud.gateway.authorization.AuthorizationManager;
|
||||
import cn.crtech.cloud.gateway.component.RestAuthenticationEntryPoint;
|
||||
import cn.crtech.cloud.gateway.component.RestfulAccessDeniedHandler;
|
||||
import cn.crtech.cloud.gateway.filter.IgnoreUrlsRemoveJwtFilter;
|
||||
import cn.hutool.core.util.ArrayUtil;
|
||||
import lombok.AllArgsConstructor;
|
||||
import org.springframework.context.annotation.Bean;
|
||||
import org.springframework.context.annotation.Configuration;
|
||||
import org.springframework.core.convert.converter.Converter;
|
||||
import org.springframework.security.authentication.AbstractAuthenticationToken;
|
||||
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.oauth2.jwt.Jwt;
|
||||
import org.springframework.security.oauth2.server.resource.authentication.JwtAuthenticationConverter;
|
||||
import org.springframework.security.oauth2.server.resource.authentication.JwtGrantedAuthoritiesConverter;
|
||||
import org.springframework.security.oauth2.server.resource.authentication.ReactiveJwtAuthenticationConverterAdapter;
|
||||
import org.springframework.security.web.server.SecurityWebFilterChain;
|
||||
import reactor.core.publisher.Mono;
|
||||
|
||||
/**
|
||||
* 资源服务器配置
|
||||
*/
|
||||
|
||||
@AllArgsConstructor
|
||||
@Configuration
|
||||
@EnableWebFluxSecurity
|
||||
public class ResourceServerConfig {
|
||||
private final AuthorizationManager authorizationManager;
|
||||
private final IgnoreUrlsConfig ignoreUrlsConfig;
|
||||
private final RestfulAccessDeniedHandler restfulAccessDeniedHandler;
|
||||
private final RestAuthenticationEntryPoint restAuthenticationEntryPoint;
|
||||
private final IgnoreUrlsRemoveJwtFilter ignoreUrlsRemoveJwtFilter;
|
||||
|
||||
@Bean
|
||||
public SecurityWebFilterChain springSecurityFilterChain(ServerHttpSecurity http) {
|
||||
http.oauth2ResourceServer().jwt()
|
||||
.jwtAuthenticationConverter(jwtAuthenticationConverter());
|
||||
//自定义处理JWT请求头过期或签名错误的结果
|
||||
http.oauth2ResourceServer().authenticationEntryPoint(restAuthenticationEntryPoint);
|
||||
//对白名单路径,直接移除JWT请求头
|
||||
http.addFilterBefore(ignoreUrlsRemoveJwtFilter, SecurityWebFiltersOrder.AUTHENTICATION);
|
||||
http.authorizeExchange()
|
||||
.pathMatchers(ArrayUtil.toArray(ignoreUrlsConfig.getUrls(), String.class)).permitAll()//白名单配置
|
||||
.anyExchange().access(authorizationManager)//配置自定义的鉴权管理器
|
||||
.and().exceptionHandling()
|
||||
.accessDeniedHandler(restfulAccessDeniedHandler)//处理未授权
|
||||
// 无访问权限handler
|
||||
.authenticationEntryPoint(restAuthenticationEntryPoint)//处理未认证
|
||||
.and().csrf().disable();
|
||||
return http.build();
|
||||
}
|
||||
|
||||
@Bean
|
||||
public Converter<Jwt, ? extends Mono<? extends AbstractAuthenticationToken>> jwtAuthenticationConverter() {
|
||||
JwtGrantedAuthoritiesConverter jwtGrantedAuthoritiesConverter = new JwtGrantedAuthoritiesConverter();
|
||||
jwtGrantedAuthoritiesConverter.setAuthorityPrefix(AuthConstant.AUTHORITY_PREFIX);
|
||||
jwtGrantedAuthoritiesConverter.setAuthoritiesClaimName(AuthConstant.AUTHORITY_CLAIM_NAME);
|
||||
JwtAuthenticationConverter jwtAuthenticationConverter = new JwtAuthenticationConverter();
|
||||
jwtAuthenticationConverter.setJwtGrantedAuthoritiesConverter(jwtGrantedAuthoritiesConverter);
|
||||
return new ReactiveJwtAuthenticationConverterAdapter(jwtAuthenticationConverter);
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,51 @@
|
||||
package cn.crtech.cloud.gateway.dto;
|
||||
|
||||
import cn.crtech.cloud.common.annotation.DataExportAnnotation;
|
||||
import com.fasterxml.jackson.annotation.JsonFormat;
|
||||
|
||||
import java.io.Serializable;
|
||||
import java.util.Date;
|
||||
import lombok.*;
|
||||
|
||||
/**
|
||||
* 授权产品实体DTO
|
||||
*
|
||||
* @author TYP
|
||||
* @since 2023-08-08 10:43
|
||||
*/
|
||||
|
||||
@Data
|
||||
@Builder
|
||||
@ToString
|
||||
@NoArgsConstructor
|
||||
@AllArgsConstructor
|
||||
public class MisAppDto implements Serializable {
|
||||
@DataExportAnnotation("产品名称")
|
||||
private String name;
|
||||
|
||||
@DataExportAnnotation("产品标识")
|
||||
private String code;
|
||||
|
||||
@DataExportAnnotation("产品描述信息")
|
||||
private String description;
|
||||
|
||||
@DataExportAnnotation("是否默认展示在产品展示列表")
|
||||
private Boolean isShow;
|
||||
|
||||
@DataExportAnnotation("授权过期时间")
|
||||
@JsonFormat(pattern = "YYYY-MM-dd", locale = "cn", timezone = "GMT+8")
|
||||
private Date expireDate;
|
||||
|
||||
@DataExportAnnotation("授权产品系列ID")
|
||||
private Integer seriesId;
|
||||
|
||||
@DataExportAnnotation("授权产品系列标识")
|
||||
private String seriesCode;
|
||||
|
||||
@DataExportAnnotation("授权产品版本ID")
|
||||
private Integer versionId;
|
||||
|
||||
@DataExportAnnotation("授权产品版本标识")
|
||||
private String version;
|
||||
|
||||
}
|
||||
@ -0,0 +1,56 @@
|
||||
package cn.crtech.cloud.gateway.dto;
|
||||
|
||||
import cn.crtech.cloud.common.annotation.DataExportAnnotation;
|
||||
import lombok.*;
|
||||
|
||||
import java.io.Serializable;
|
||||
|
||||
/**
|
||||
* desc
|
||||
*
|
||||
* @author TYP
|
||||
* @since 2023-08-08 10:20
|
||||
*/
|
||||
|
||||
@Data
|
||||
@Builder
|
||||
@ToString
|
||||
@NoArgsConstructor
|
||||
@AllArgsConstructor
|
||||
public class MisComUserDto implements Serializable {
|
||||
@DataExportAnnotation("公司标识")
|
||||
private String companyCode;
|
||||
|
||||
@DataExportAnnotation("公司名称")
|
||||
private String companyName;
|
||||
|
||||
@DataExportAnnotation("公司图标")
|
||||
private String logo;
|
||||
|
||||
@DataExportAnnotation("所在省地址")
|
||||
private String provinceName;
|
||||
|
||||
@DataExportAnnotation("所在市地址")
|
||||
private String cityName;
|
||||
|
||||
@DataExportAnnotation("所在区/县地址")
|
||||
private String countyName;
|
||||
|
||||
@DataExportAnnotation("详细地址")
|
||||
private String address;
|
||||
|
||||
@DataExportAnnotation("用户ID")
|
||||
private Integer userId;
|
||||
|
||||
@DataExportAnnotation("是否公司所有人")
|
||||
private Boolean isOwner;
|
||||
|
||||
@DataExportAnnotation("职位")
|
||||
private String position;
|
||||
|
||||
@DataExportAnnotation("是否默认公司")
|
||||
private Boolean isDefault;
|
||||
|
||||
@DataExportAnnotation("状态")
|
||||
private Integer state;
|
||||
}
|
||||
@ -0,0 +1,35 @@
|
||||
package cn.crtech.cloud.gateway.dto;
|
||||
|
||||
import cn.crtech.cloud.common.annotation.DataExportAnnotation;
|
||||
|
||||
import java.io.Serializable;
|
||||
import lombok.*;
|
||||
|
||||
/**
|
||||
* 菜单权限功能实体
|
||||
*
|
||||
* @author TYP
|
||||
* @since 2023-07-17 14:42
|
||||
*/
|
||||
|
||||
@Data
|
||||
@Builder
|
||||
@ToString
|
||||
@NoArgsConstructor
|
||||
@AllArgsConstructor
|
||||
public class MisPopedomAuthorityDto implements Serializable {
|
||||
@DataExportAnnotation("主键id")
|
||||
private Integer id;
|
||||
|
||||
@DataExportAnnotation("菜单ID")
|
||||
private Integer popedomId;
|
||||
|
||||
@DataExportAnnotation("权限名称")
|
||||
private String name;
|
||||
|
||||
@DataExportAnnotation("路由权限功能标识")
|
||||
private String authorityCode;
|
||||
|
||||
@DataExportAnnotation("产品标识")
|
||||
private String applicationCode;
|
||||
}
|
||||
@ -0,0 +1,62 @@
|
||||
package cn.crtech.cloud.gateway.dto;
|
||||
|
||||
import cn.crtech.cloud.common.annotation.DataExportAnnotation;
|
||||
import cn.crtech.cloud.common.pojo.Tree;
|
||||
|
||||
import java.io.Serializable;
|
||||
import java.util.List;
|
||||
import lombok.*;
|
||||
import org.apache.commons.lang3.ObjectUtils;
|
||||
|
||||
/**
|
||||
* 产品版本实体
|
||||
*
|
||||
* @author TYP
|
||||
* @since 2023-07-14 14:48
|
||||
*/
|
||||
|
||||
@Data
|
||||
@Builder
|
||||
@ToString
|
||||
@NoArgsConstructor
|
||||
@AllArgsConstructor
|
||||
@EqualsAndHashCode(callSuper = true)
|
||||
public class MisPopedomDto extends Tree implements Serializable {
|
||||
@DataExportAnnotation("主键id")
|
||||
private Integer id;
|
||||
|
||||
@DataExportAnnotation("菜单名称")
|
||||
private String name;
|
||||
|
||||
@DataExportAnnotation("父级菜单ID")
|
||||
private Integer parentId;
|
||||
|
||||
@DataExportAnnotation("菜单路由跳转地址")
|
||||
private String route;
|
||||
|
||||
@DataExportAnnotation("菜单图标")
|
||||
private String icon;
|
||||
|
||||
@DataExportAnnotation("菜单图标(bootstrap专用)")
|
||||
private String bIcon;
|
||||
|
||||
@DataExportAnnotation("菜单类型 0菜单目录 1跳转菜单")
|
||||
private Integer menuType;
|
||||
|
||||
@DataExportAnnotation("菜单排序值")
|
||||
private Integer orderNo;
|
||||
|
||||
@DataExportAnnotation("路由菜单类型")
|
||||
private Integer isMenu;
|
||||
|
||||
@DataExportAnnotation("产品标识")
|
||||
private String applicationCode;
|
||||
|
||||
@DataExportAnnotation("产品特殊权限")
|
||||
private List<MisPopedomAuthorityDto> authorityList;
|
||||
|
||||
@Override
|
||||
public Boolean isRoot() {
|
||||
return ObjectUtils.isEmpty(this.parentId);
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,57 @@
|
||||
package cn.crtech.cloud.gateway.dto;
|
||||
|
||||
import cn.crtech.cloud.common.annotation.DataExportAnnotation;
|
||||
|
||||
import java.io.Serializable;
|
||||
import java.util.List;
|
||||
import lombok.*;
|
||||
|
||||
/**
|
||||
* 用户信息实体DTO
|
||||
*
|
||||
* @author TYP
|
||||
* @since 2023-08-08 9:59
|
||||
*/
|
||||
|
||||
@Data
|
||||
@Builder
|
||||
@ToString
|
||||
@NoArgsConstructor
|
||||
@AllArgsConstructor
|
||||
public class MisUserInfoDto implements Serializable {
|
||||
@DataExportAnnotation("用户ID")
|
||||
private Integer id;
|
||||
|
||||
@DataExportAnnotation("用户名称")
|
||||
private String name;
|
||||
|
||||
@DataExportAnnotation("用户邮箱地址")
|
||||
private String email;
|
||||
|
||||
@DataExportAnnotation("用户手机号码")
|
||||
private String mobile;
|
||||
|
||||
@DataExportAnnotation("用户所属公司标识")
|
||||
private String companyCode;
|
||||
|
||||
@DataExportAnnotation("是否所属公司拥有人")
|
||||
private Boolean isOwner;
|
||||
|
||||
@DataExportAnnotation("当前登录公司信息")
|
||||
private MisComUserDto company;
|
||||
|
||||
@DataExportAnnotation("已授权角色名字集合")
|
||||
private List<String> roles;
|
||||
|
||||
@DataExportAnnotation("用户所有公司数据集合")
|
||||
private List<MisComUserDto> companys;
|
||||
|
||||
@DataExportAnnotation("当前选中产品")
|
||||
private MisAppDto application;
|
||||
|
||||
@DataExportAnnotation("用户所有授权产品数据集合")
|
||||
private List<MisAppDto> applications;
|
||||
|
||||
@DataExportAnnotation("用户应用授权路由菜单")
|
||||
private List<MisPopedomDto> routes;
|
||||
}
|
||||
@ -0,0 +1,57 @@
|
||||
package cn.crtech.cloud.gateway.filter;
|
||||
|
||||
import cn.hutool.core.util.StrUtil;
|
||||
import com.nimbusds.jose.JWSObject;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
import org.springframework.cloud.gateway.filter.GatewayFilterChain;
|
||||
import org.springframework.cloud.gateway.filter.GlobalFilter;
|
||||
import org.springframework.core.Ordered;
|
||||
import org.springframework.http.server.reactive.ServerHttpRequest;
|
||||
import org.springframework.stereotype.Component;
|
||||
import org.springframework.web.server.ServerWebExchange;
|
||||
import reactor.core.publisher.Mono;
|
||||
|
||||
import java.io.UnsupportedEncodingException;
|
||||
import java.net.URLEncoder;
|
||||
import java.text.ParseException;
|
||||
|
||||
/**
|
||||
* 将登录用户的JWT转化成用户信息的全局过滤器
|
||||
*/
|
||||
@Component
|
||||
public class AuthGlobalFilter implements GlobalFilter, Ordered {
|
||||
private static Logger LOGGER = LoggerFactory.getLogger(AuthGlobalFilter.class);
|
||||
|
||||
@Override
|
||||
public Mono<Void> filter(ServerWebExchange exchange, GatewayFilterChain chain) {
|
||||
String token = exchange.getRequest().getHeaders().getFirst("Authorization");
|
||||
if (StrUtil.isEmpty(token)) {
|
||||
return chain.filter(exchange);
|
||||
}
|
||||
try {
|
||||
//从token中解析用户信息并设置到Header中去
|
||||
String realToken = token.replace("Bearer ", "");
|
||||
JWSObject jwsObject = JWSObject.parse(realToken);
|
||||
String userStr = jwsObject.getPayload().toString();
|
||||
LOGGER.info("AuthGlobalFilter.filter() user:{}", userStr);
|
||||
String userStrEncode = null;
|
||||
try {
|
||||
userStrEncode = URLEncoder.encode(userStr, "UTF-8");
|
||||
} catch (UnsupportedEncodingException e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
|
||||
ServerHttpRequest request = exchange.getRequest().mutate().header("user", userStrEncode).build();
|
||||
exchange = exchange.mutate().request(request).build();
|
||||
} catch (ParseException e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
return chain.filter(exchange);
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getOrder() {
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,46 @@
|
||||
package cn.crtech.cloud.gateway.filter;
|
||||
|
||||
import cn.crtech.cloud.gateway.config.IgnoreUrlsConfig;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.http.server.reactive.ServerHttpRequest;
|
||||
import org.springframework.stereotype.Component;
|
||||
import org.springframework.util.AntPathMatcher;
|
||||
import org.springframework.util.PathMatcher;
|
||||
import org.springframework.web.server.ServerWebExchange;
|
||||
import org.springframework.web.server.WebFilter;
|
||||
import org.springframework.web.server.WebFilterChain;
|
||||
import reactor.core.publisher.Mono;
|
||||
|
||||
import java.net.URI;
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* 白名单路径访问时需要移除JWT请求头
|
||||
*/
|
||||
|
||||
@Component
|
||||
public class IgnoreUrlsRemoveJwtFilter implements WebFilter {
|
||||
private IgnoreUrlsConfig ignoreUrlsConfig;
|
||||
|
||||
@Autowired
|
||||
private void setIgnoreUrlsConfig(IgnoreUrlsConfig ignoreUrlsConfig) {
|
||||
this.ignoreUrlsConfig = ignoreUrlsConfig;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Mono<Void> filter(ServerWebExchange exchange, WebFilterChain chain) {
|
||||
ServerHttpRequest request = exchange.getRequest();
|
||||
URI uri = request.getURI();
|
||||
PathMatcher pathMatcher = new AntPathMatcher();
|
||||
//白名单路径移除JWT请求头
|
||||
List<String> ignoreUrls = ignoreUrlsConfig.getUrls();
|
||||
for (String ignoreUrl : ignoreUrls) {
|
||||
if (pathMatcher.match(ignoreUrl, uri.getPath())) {
|
||||
request = exchange.getRequest().mutate().header("Authorization", "").build();
|
||||
exchange = exchange.mutate().request(request).build();
|
||||
return chain.filter(exchange);
|
||||
}
|
||||
}
|
||||
return chain.filter(exchange);
|
||||
}
|
||||
}
|
||||
127
Gateway/src/main/resources/application-dev.yml
Normal file
127
Gateway/src/main/resources/application-dev.yml
Normal file
@ -0,0 +1,127 @@
|
||||
server:
|
||||
port: 7001
|
||||
servlet:
|
||||
encoding:
|
||||
charset: utf-8
|
||||
enabled: true
|
||||
force: true
|
||||
tomcat:
|
||||
uri-encoding: UTF-8
|
||||
logging:
|
||||
config: classpath:logback.xml
|
||||
file:
|
||||
path: logs/crtech-cloud-gateway.log
|
||||
level:
|
||||
cn.crtech.cloud.gateway: debug
|
||||
spring:
|
||||
application:
|
||||
name: crtech-cloud-gateway
|
||||
cloud:
|
||||
nacos:
|
||||
discovery:
|
||||
server-addr: localhost:8848
|
||||
gateway:
|
||||
routes: #配置路由规则 短横线必须对齐routes
|
||||
- id: auth-route
|
||||
uri: lb://crtech-cloud-auth
|
||||
predicates:
|
||||
- Path=/auth/**
|
||||
filters:
|
||||
- StripPrefix=1
|
||||
- id: general-route
|
||||
uri: lb://crtech-cloud-general
|
||||
predicates:
|
||||
- Path=/general/**
|
||||
filters:
|
||||
- StripPrefix=1
|
||||
- id: rm-route
|
||||
uri: lb://crtech-cloud-resmanager
|
||||
predicates:
|
||||
- Path=/rm/**
|
||||
filters:
|
||||
- StripPrefix=1
|
||||
- id: mis-route
|
||||
uri: lb://crtech-cloud-mis
|
||||
predicates:
|
||||
- Path=/mis/**
|
||||
filters:
|
||||
- StripPrefix=1
|
||||
- id: lms-route
|
||||
uri: lb://crtech-cloud-lms
|
||||
predicates:
|
||||
- Path=/lms/**
|
||||
filters:
|
||||
- StripPrefix=1
|
||||
- id: pivas-kms-route
|
||||
uri: lb://crtech-cloud-pivas-kms
|
||||
predicates:
|
||||
- Path=/pivaskms/**
|
||||
filters:
|
||||
- StripPrefix=1
|
||||
- id: crtech-cloud-pivas-tm-route
|
||||
uri: lb://crtech-cloud-pivas-tm
|
||||
predicates:
|
||||
- Path=/pivastm/**
|
||||
filters:
|
||||
- StripPrefix=1
|
||||
- id: crtech-cloud-main-route
|
||||
uri: lb://crtech-cloud-main
|
||||
predicates:
|
||||
- Path=/main/**
|
||||
filters:
|
||||
- StripPrefix=1
|
||||
- id: pivas-customer-route
|
||||
uri: lb://crtech-cloud-customer
|
||||
predicates:
|
||||
- Path=/customer/**
|
||||
filters:
|
||||
- StripPrefix=1
|
||||
- id: lserp-invoice-route
|
||||
uri: lb://lserp-invoice
|
||||
predicates:
|
||||
- Path=/lserp-invoice/**
|
||||
filters:
|
||||
- StripPrefix=1
|
||||
discovery:
|
||||
locator:
|
||||
enabled: true #开启从注册中心动态创建路由的功能
|
||||
lower-case-service-id: true #使用小写服务名,默认是大写
|
||||
globalcors:
|
||||
add-to-simple-url-handler-mapping: true
|
||||
corsConfigurations:
|
||||
'[/**]':
|
||||
# 支持跨域访问的来源
|
||||
allowedOrigins: "*"
|
||||
# 切记 allowCredentials 配置 为true时,allowedOrigins不能为 *
|
||||
#allowCredentials: true
|
||||
# 浏览器跨域嗅探间隔 单位秒
|
||||
maxAge: 86400
|
||||
# 支持的方法 * 代表所有
|
||||
allowedMethods: "*"
|
||||
allowedHeaders: "*"
|
||||
#exposedHeaders: "setToken"
|
||||
security:
|
||||
oauth2:
|
||||
resourceserver:
|
||||
jwt:
|
||||
jwk-set-uri: 'http://localhost:9401/rsa/publicKey' #配置RSA的公钥访问地址
|
||||
redis:
|
||||
database: 4
|
||||
port: 6379
|
||||
host: localhost
|
||||
password:
|
||||
secure:
|
||||
ignore:
|
||||
urls: #配置白名单路径, actuator spingboot的健康检查
|
||||
- "/actuator/**"
|
||||
- "/customer/wx/cp/**"
|
||||
- "/customer/wx/mp/**"
|
||||
- "/customer/open/**"
|
||||
- "/main/home/**"
|
||||
- "/auth/oauth/token"
|
||||
- "/auth/oauth/initRedis"
|
||||
- "/auth/oauth/logout"
|
||||
- "/mis/system/uploadAddressData"
|
||||
- "/mis/system/uploadIconData"
|
||||
- "/mis/ls/erp/**"
|
||||
- "/lserp-invoice/no/auth/**"
|
||||
6
Gateway/src/main/resources/bootstrap.yml
Normal file
6
Gateway/src/main/resources/bootstrap.yml
Normal file
@ -0,0 +1,6 @@
|
||||
spring:
|
||||
profiles:
|
||||
active: dev
|
||||
|
||||
|
||||
|
||||
56
Gateway/src/main/resources/logback.xml
Normal file
56
Gateway/src/main/resources/logback.xml
Normal file
@ -0,0 +1,56 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<configuration>
|
||||
<appender name="FILE"
|
||||
class="ch.qos.logback.core.rolling.RollingFileAppender">
|
||||
|
||||
<rollingPolicy class="ch.qos.logback.core.rolling.TimeBasedRollingPolicy">
|
||||
<fileNamePattern>logs/crtech-cloud-gateway.%d{yyyy-MM-dd}.log
|
||||
</fileNamePattern>
|
||||
</rollingPolicy>
|
||||
|
||||
<encoder>
|
||||
<pattern>%d{yyyy-MM-dd_HH:mm:ss} %logger{18} -%msg%n
|
||||
</pattern>
|
||||
</encoder>
|
||||
</appender>
|
||||
|
||||
<appender name="STDOUT" class="ch.qos.logback.core.ConsoleAppender">
|
||||
|
||||
<encoder>
|
||||
<pattern>%d{HH:mm:ss.SSS} [%thread] %-5level %logger{36} - %msg%n
|
||||
</pattern>
|
||||
</encoder>
|
||||
</appender>
|
||||
|
||||
<appender name="ASYNC" class="ch.qos.logback.classic.AsyncAppender">
|
||||
<discardingThreshold>0</discardingThreshold>
|
||||
<queueSize>1000</queueSize>
|
||||
<appender-ref ref="FILE" />
|
||||
</appender>
|
||||
|
||||
<logger name="org" level="info" additivity="false">
|
||||
<appender-ref ref="FILE"></appender-ref>
|
||||
<appender-ref ref="STDOUT"></appender-ref>
|
||||
</logger>
|
||||
<logger name="com" level="info" additivity="false">
|
||||
<appender-ref ref="FILE"></appender-ref>
|
||||
<appender-ref ref="STDOUT"></appender-ref>
|
||||
</logger>
|
||||
<logger name="net" level="info" additivity="false">
|
||||
<appender-ref ref="FILE"></appender-ref>
|
||||
<appender-ref ref="STDOUT"></appender-ref>
|
||||
</logger>
|
||||
|
||||
<logger name="com.netflix" level="debug" additivity="false">
|
||||
<appender-ref ref="STDOUT"></appender-ref>
|
||||
<appender-ref ref="FILE"></appender-ref>
|
||||
</logger>
|
||||
|
||||
<logger name="cn.crtech.cloud.gateway" level="debug" additivity="false">
|
||||
<appender-ref ref="STDOUT"></appender-ref>
|
||||
<appender-ref ref="FILE"></appender-ref>
|
||||
</logger>
|
||||
<root level="INFO">
|
||||
<appender-ref ref="ASYNC" />
|
||||
</root>
|
||||
</configuration>
|
||||
Reference in New Issue
Block a user