From 3e05e32d4bc085805355e91fab2d01da9a346d14 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?=E7=94=B0=E8=89=BA=E9=B9=8F?= <18382071280@163.com>
Date: Wed, 23 Aug 2023 16:37:12 +0800
Subject: [PATCH] =?UTF-8?q?fix(*)=20=E9=A6=96=E6=AC=A1=E6=8F=90=E4=BA=A4Ge?=
=?UTF-8?q?neral=E9=A1=B9=E7=9B=AE?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
---
.gitignore | 1 +
Gateway/pom.xml | 114 +++++++
.../cloud/gateway/GatewayApplication.java | 20 ++
.../authorization/AuthorizationManager.java | 282 ++++++++++++++++++
.../RestAuthenticationEntryPoint.java | 33 ++
.../component/RestfulAccessDeniedHandler.java | 33 ++
.../cloud/gateway/config/CorsConfig.java | 31 ++
.../gateway/config/IgnoreUrlsConfig.java | 19 ++
.../gateway/config/RedisRepositoryConfig.java | 31 ++
.../gateway/config/ResourceServerConfig.java | 66 ++++
.../crtech/cloud/gateway/dto/MisAppDto.java | 51 ++++
.../cloud/gateway/dto/MisComUserDto.java | 56 ++++
.../gateway/dto/MisPopedomAuthorityDto.java | 35 +++
.../cloud/gateway/dto/MisPopedomDto.java | 62 ++++
.../cloud/gateway/dto/MisUserInfoDto.java | 57 ++++
.../gateway/filter/AuthGlobalFilter.java | 57 ++++
.../filter/IgnoreUrlsRemoveJwtFilter.java | 46 +++
.../src/main/resources/application-dev.yml | 127 ++++++++
Gateway/src/main/resources/bootstrap.yml | 6 +
Gateway/src/main/resources/logback.xml | 56 ++++
20 files changed, 1183 insertions(+)
create mode 100644 Gateway/pom.xml
create mode 100644 Gateway/src/main/java/cn/crtech/cloud/gateway/GatewayApplication.java
create mode 100644 Gateway/src/main/java/cn/crtech/cloud/gateway/authorization/AuthorizationManager.java
create mode 100644 Gateway/src/main/java/cn/crtech/cloud/gateway/component/RestAuthenticationEntryPoint.java
create mode 100644 Gateway/src/main/java/cn/crtech/cloud/gateway/component/RestfulAccessDeniedHandler.java
create mode 100644 Gateway/src/main/java/cn/crtech/cloud/gateway/config/CorsConfig.java
create mode 100644 Gateway/src/main/java/cn/crtech/cloud/gateway/config/IgnoreUrlsConfig.java
create mode 100644 Gateway/src/main/java/cn/crtech/cloud/gateway/config/RedisRepositoryConfig.java
create mode 100644 Gateway/src/main/java/cn/crtech/cloud/gateway/config/ResourceServerConfig.java
create mode 100644 Gateway/src/main/java/cn/crtech/cloud/gateway/dto/MisAppDto.java
create mode 100644 Gateway/src/main/java/cn/crtech/cloud/gateway/dto/MisComUserDto.java
create mode 100644 Gateway/src/main/java/cn/crtech/cloud/gateway/dto/MisPopedomAuthorityDto.java
create mode 100644 Gateway/src/main/java/cn/crtech/cloud/gateway/dto/MisPopedomDto.java
create mode 100644 Gateway/src/main/java/cn/crtech/cloud/gateway/dto/MisUserInfoDto.java
create mode 100644 Gateway/src/main/java/cn/crtech/cloud/gateway/filter/AuthGlobalFilter.java
create mode 100644 Gateway/src/main/java/cn/crtech/cloud/gateway/filter/IgnoreUrlsRemoveJwtFilter.java
create mode 100644 Gateway/src/main/resources/application-dev.yml
create mode 100644 Gateway/src/main/resources/bootstrap.yml
create mode 100644 Gateway/src/main/resources/logback.xml
diff --git a/.gitignore b/.gitignore
index ae31e7f..da05752 100644
--- a/.gitignore
+++ b/.gitignore
@@ -22,3 +22,4 @@
/Auth/src/main/resources/bootstrap-test.yml
/Feign/src/main/resources/application-test.yml
/Feign/src/main/resources/bootstrap-test.yml
+/Gateway/src/main/resources/application-test.yml
diff --git a/Gateway/pom.xml b/Gateway/pom.xml
new file mode 100644
index 0000000..2ceb7a4
--- /dev/null
+++ b/Gateway/pom.xml
@@ -0,0 +1,114 @@
+
+
+ 4.0.0
+
+ cn.crtech.cloud.gateway
+ Gateway
+ 1.0.1
+
+
+
+ cn.crtech.cloud.dependencies
+ Dependencies
+ 1.0.1
+
+
+
+
+
+ 8
+ 8
+ 1.0.1
+
+
+
+
+ cn.crtech.cloud.common
+ Common
+ ${common.version}
+
+
+
+ org.springframework.cloud
+ spring-cloud-starter-gateway
+
+
+
+ org.apache.commons
+ commons-lang3
+
+
+
+
+ com.alibaba.cloud
+ spring-cloud-starter-alibaba-nacos-discovery
+
+
+
+ org.springframework.security
+ spring-security-config
+
+
+
+ org.springframework.security
+ spring-security-oauth2-resource-server
+
+
+
+ org.springframework.security
+ spring-security-oauth2-client
+
+
+
+ org.springframework.security
+ spring-security-oauth2-jose
+
+
+
+
+ org.springframework.boot
+ spring-boot-configuration-processor
+ true
+
+
+
+ org.springframework.cloud
+ spring-cloud-starter-loadbalancer
+
+
+
+ org.springframework.cloud
+ spring-cloud-starter-openfeign
+
+
+
+ org.springframework.boot
+ spring-boot-starter-data-redis
+
+
+ io.lettuce
+ lettuce-core
+
+
+
+
+
+ redis.clients
+ jedis
+
+
+
+
+
+
+ org.springframework.boot
+ spring-boot-maven-plugin
+
+ -Dfile.encoding=UTF-8
+
+
+
+
+
diff --git a/Gateway/src/main/java/cn/crtech/cloud/gateway/GatewayApplication.java b/Gateway/src/main/java/cn/crtech/cloud/gateway/GatewayApplication.java
new file mode 100644
index 0000000..d00fc31
--- /dev/null
+++ b/Gateway/src/main/java/cn/crtech/cloud/gateway/GatewayApplication.java
@@ -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);
+ }
+}
diff --git a/Gateway/src/main/java/cn/crtech/cloud/gateway/authorization/AuthorizationManager.java b/Gateway/src/main/java/cn/crtech/cloud/gateway/authorization/AuthorizationManager.java
new file mode 100644
index 0000000..b24728f
--- /dev/null
+++ b/Gateway/src/main/java/cn/crtech/cloud/gateway/authorization/AuthorizationManager.java
@@ -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 {
+ private RedisTemplate redisTemplate;
+
+ @Autowired
+ private void setRedisTemplate(RedisTemplate redisTemplate) {
+ this.redisTemplate = redisTemplate;
+ }
+
+ private static Logger LOGGER = LoggerFactory.getLogger(AuthGlobalFilter.class);
+
+ @Override
+ public Mono check(Mono 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 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 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 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 getFreeAuthority(MisAppDto thisApp, String clientId, String requestURI, List 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 getAppFeign(String realPath, List authorities) {
+ List 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 getAuthorityData(MisAppDto thisApp, String redisName, String realPath, List authorities) {
+ List 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] : "";
+ }
+}
diff --git a/Gateway/src/main/java/cn/crtech/cloud/gateway/component/RestAuthenticationEntryPoint.java b/Gateway/src/main/java/cn/crtech/cloud/gateway/component/RestAuthenticationEntryPoint.java
new file mode 100644
index 0000000..e6794d1
--- /dev/null
+++ b/Gateway/src/main/java/cn/crtech/cloud/gateway/component/RestAuthenticationEntryPoint.java
@@ -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 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));
+ }
+}
diff --git a/Gateway/src/main/java/cn/crtech/cloud/gateway/component/RestfulAccessDeniedHandler.java b/Gateway/src/main/java/cn/crtech/cloud/gateway/component/RestfulAccessDeniedHandler.java
new file mode 100644
index 0000000..63e07a1
--- /dev/null
+++ b/Gateway/src/main/java/cn/crtech/cloud/gateway/component/RestfulAccessDeniedHandler.java
@@ -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 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));
+ }
+}
diff --git a/Gateway/src/main/java/cn/crtech/cloud/gateway/config/CorsConfig.java b/Gateway/src/main/java/cn/crtech/cloud/gateway/config/CorsConfig.java
new file mode 100644
index 0000000..d9b61c5
--- /dev/null
+++ b/Gateway/src/main/java/cn/crtech/cloud/gateway/config/CorsConfig.java
@@ -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);
+ }
+}
diff --git a/Gateway/src/main/java/cn/crtech/cloud/gateway/config/IgnoreUrlsConfig.java b/Gateway/src/main/java/cn/crtech/cloud/gateway/config/IgnoreUrlsConfig.java
new file mode 100644
index 0000000..942adbf
--- /dev/null
+++ b/Gateway/src/main/java/cn/crtech/cloud/gateway/config/IgnoreUrlsConfig.java
@@ -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 urls;
+}
diff --git a/Gateway/src/main/java/cn/crtech/cloud/gateway/config/RedisRepositoryConfig.java b/Gateway/src/main/java/cn/crtech/cloud/gateway/config/RedisRepositoryConfig.java
new file mode 100644
index 0000000..be800b4
--- /dev/null
+++ b/Gateway/src/main/java/cn/crtech/cloud/gateway/config/RedisRepositoryConfig.java
@@ -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 redisTemplate(RedisConnectionFactory connectionFactory) {
+ RedisTemplate 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;
+ }
+}
diff --git a/Gateway/src/main/java/cn/crtech/cloud/gateway/config/ResourceServerConfig.java b/Gateway/src/main/java/cn/crtech/cloud/gateway/config/ResourceServerConfig.java
new file mode 100644
index 0000000..97e8617
--- /dev/null
+++ b/Gateway/src/main/java/cn/crtech/cloud/gateway/config/ResourceServerConfig.java
@@ -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> 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);
+ }
+}
diff --git a/Gateway/src/main/java/cn/crtech/cloud/gateway/dto/MisAppDto.java b/Gateway/src/main/java/cn/crtech/cloud/gateway/dto/MisAppDto.java
new file mode 100644
index 0000000..6cea0d9
--- /dev/null
+++ b/Gateway/src/main/java/cn/crtech/cloud/gateway/dto/MisAppDto.java
@@ -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;
+
+}
diff --git a/Gateway/src/main/java/cn/crtech/cloud/gateway/dto/MisComUserDto.java b/Gateway/src/main/java/cn/crtech/cloud/gateway/dto/MisComUserDto.java
new file mode 100644
index 0000000..9538e4d
--- /dev/null
+++ b/Gateway/src/main/java/cn/crtech/cloud/gateway/dto/MisComUserDto.java
@@ -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;
+}
diff --git a/Gateway/src/main/java/cn/crtech/cloud/gateway/dto/MisPopedomAuthorityDto.java b/Gateway/src/main/java/cn/crtech/cloud/gateway/dto/MisPopedomAuthorityDto.java
new file mode 100644
index 0000000..1e1df54
--- /dev/null
+++ b/Gateway/src/main/java/cn/crtech/cloud/gateway/dto/MisPopedomAuthorityDto.java
@@ -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;
+}
diff --git a/Gateway/src/main/java/cn/crtech/cloud/gateway/dto/MisPopedomDto.java b/Gateway/src/main/java/cn/crtech/cloud/gateway/dto/MisPopedomDto.java
new file mode 100644
index 0000000..ec1714c
--- /dev/null
+++ b/Gateway/src/main/java/cn/crtech/cloud/gateway/dto/MisPopedomDto.java
@@ -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 authorityList;
+
+ @Override
+ public Boolean isRoot() {
+ return ObjectUtils.isEmpty(this.parentId);
+ }
+}
diff --git a/Gateway/src/main/java/cn/crtech/cloud/gateway/dto/MisUserInfoDto.java b/Gateway/src/main/java/cn/crtech/cloud/gateway/dto/MisUserInfoDto.java
new file mode 100644
index 0000000..3c987d8
--- /dev/null
+++ b/Gateway/src/main/java/cn/crtech/cloud/gateway/dto/MisUserInfoDto.java
@@ -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 roles;
+
+ @DataExportAnnotation("用户所有公司数据集合")
+ private List companys;
+
+ @DataExportAnnotation("当前选中产品")
+ private MisAppDto application;
+
+ @DataExportAnnotation("用户所有授权产品数据集合")
+ private List applications;
+
+ @DataExportAnnotation("用户应用授权路由菜单")
+ private List routes;
+}
diff --git a/Gateway/src/main/java/cn/crtech/cloud/gateway/filter/AuthGlobalFilter.java b/Gateway/src/main/java/cn/crtech/cloud/gateway/filter/AuthGlobalFilter.java
new file mode 100644
index 0000000..3d2ed05
--- /dev/null
+++ b/Gateway/src/main/java/cn/crtech/cloud/gateway/filter/AuthGlobalFilter.java
@@ -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 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;
+ }
+}
diff --git a/Gateway/src/main/java/cn/crtech/cloud/gateway/filter/IgnoreUrlsRemoveJwtFilter.java b/Gateway/src/main/java/cn/crtech/cloud/gateway/filter/IgnoreUrlsRemoveJwtFilter.java
new file mode 100644
index 0000000..2c8372c
--- /dev/null
+++ b/Gateway/src/main/java/cn/crtech/cloud/gateway/filter/IgnoreUrlsRemoveJwtFilter.java
@@ -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 filter(ServerWebExchange exchange, WebFilterChain chain) {
+ ServerHttpRequest request = exchange.getRequest();
+ URI uri = request.getURI();
+ PathMatcher pathMatcher = new AntPathMatcher();
+ //白名单路径移除JWT请求头
+ List 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);
+ }
+}
diff --git a/Gateway/src/main/resources/application-dev.yml b/Gateway/src/main/resources/application-dev.yml
new file mode 100644
index 0000000..960bc3f
--- /dev/null
+++ b/Gateway/src/main/resources/application-dev.yml
@@ -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/**"
diff --git a/Gateway/src/main/resources/bootstrap.yml b/Gateway/src/main/resources/bootstrap.yml
new file mode 100644
index 0000000..1ab53e5
--- /dev/null
+++ b/Gateway/src/main/resources/bootstrap.yml
@@ -0,0 +1,6 @@
+spring:
+ profiles:
+ active: dev
+
+
+
diff --git a/Gateway/src/main/resources/logback.xml b/Gateway/src/main/resources/logback.xml
new file mode 100644
index 0000000..80508da
--- /dev/null
+++ b/Gateway/src/main/resources/logback.xml
@@ -0,0 +1,56 @@
+
+
+
+
+
+ logs/crtech-cloud-gateway.%d{yyyy-MM-dd}.log
+
+
+
+
+ %d{yyyy-MM-dd_HH:mm:ss} %logger{18} -%msg%n
+
+
+
+
+
+
+
+ %d{HH:mm:ss.SSS} [%thread] %-5level %logger{36} - %msg%n
+
+
+
+
+
+ 0
+ 1000
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file