From 75ad8cffbd47afc854cf0830db9ceb13dc55415b Mon Sep 17 00:00:00 2001 From: haoxr <1490493387@qq.com> Date: Thu, 8 Jul 2021 00:34:15 +0800 Subject: [PATCH] =?UTF-8?q?fix(JwtUtils):=20JWT=E8=BD=BD=E4=BD=93=E6=9C=89?= =?UTF-8?q?=E4=B8=AD=E6=96=87=E8=A7=A3=E6=9E=90=E5=87=BA=E6=9D=A5=E6=98=AF?= =?UTF-8?q?=E9=97=AE=E5=8F=B7=E4=B9=B1=E7=A0=81?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Closes I3X8V6 --- docs/nacos/DEFAULT_GROUP.zip | Bin 8183 -> 8722 bytes .../src/main/resources/bootstrap-dev.yml | 23 ----- .../auth/controller/OAuthController.java | 2 +- .../config/AuthorizationServerConfig.java | 4 +- .../service/UserDetailsServiceImpl.java | 2 +- .../src/main/resources/bootstrap-dev.yml | 30 ------ .../youlai/common/constant/AuthConstants.java | 1 - .../com/youlai/common/web/util/JwtUtils.java | 44 ++++++--- .../component/RedisChannelListener.java | 5 +- ...Cache.java => UrlPermRolesLocalCache.java} | 3 +- .../security/ResourceServerManager.java | 92 +++++++++--------- .../security/SecurityGlobalFilter.java | 23 ++--- .../gateway/swagger/SwaggerHandler.java | 2 +- .../gateway/swagger/SwaggerHeaderFilter.java | 4 +- .../src/main/resources/bootstrap-dev.yml | 24 +---- .../src/main/resources/bootstrap-prod.yml | 21 ---- 16 files changed, 99 insertions(+), 181 deletions(-) rename youlai-gateway/src/main/java/com/youlai/gateway/component/{AdminRoleLocalCache.java => UrlPermRolesLocalCache.java} (94%) diff --git a/docs/nacos/DEFAULT_GROUP.zip b/docs/nacos/DEFAULT_GROUP.zip index 68211b613c3c5f51c6595e39977c839aa0c5d1ed..c8b608e95758102e88415d12a258d0d4e3751abb 100644 GIT binary patch delta 3732 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 delta 3221 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 lupbMvAD;yr62G-hf5R!lbV + * * 委托方式,根据密码的前缀选择对应的encoder,例如:{bcypt}前缀->标识BCYPT算法加密;{noop}->标识不使用任何加密即明文的方式 * 密码判读 DaoAuthenticationProvider#additionalAuthenticationChecks - * - * @return */ @Bean public PasswordEncoder passwordEncoder() { diff --git a/youlai-auth/src/main/java/com/youlai/auth/security/service/UserDetailsServiceImpl.java b/youlai-auth/src/main/java/com/youlai/auth/security/service/UserDetailsServiceImpl.java index 3642c1051..588dd8dcb 100644 --- a/youlai-auth/src/main/java/com/youlai/auth/security/service/UserDetailsServiceImpl.java +++ b/youlai-auth/src/main/java/com/youlai/auth/security/service/UserDetailsServiceImpl.java @@ -34,7 +34,7 @@ public class UserDetailsServiceImpl implements UserDetailsService { @Override public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException { - String clientId = JwtUtils.getAuthClientId(); + String clientId = JwtUtils.getOAuthClientId(); OAuthClientEnum client = OAuthClientEnum.getByClientId(clientId); Result result; diff --git a/youlai-auth/src/main/resources/bootstrap-dev.yml b/youlai-auth/src/main/resources/bootstrap-dev.yml index 78b27f212..4c0ab6f9c 100644 --- a/youlai-auth/src/main/resources/bootstrap-dev.yml +++ b/youlai-auth/src/main/resources/bootstrap-dev.yml @@ -9,38 +9,8 @@ spring: discovery: server-addr: http://localhost:8848 config: - # docker启动nacos-server需要配置 server-addr: ${spring.cloud.nacos.discovery.server-addr} file-extension: yaml group: DEFAULT_GROUP - sentinel: - enabled: true - eager: true # 取消控制台懒加载,项目启动即连接Sentinel - transport: - client-ip: localhost - dashboard: localhost:8080 - datasource: - # 降级规则 - degrade: - nacos: - server-addr: ${spring.cloud.nacos.discovery.server-addr} - dataId: ${spring.application.name}-degrade-rules - groupId: SENTINEL_GROUP - data-type: json - rule-type: degrade -# 开启feign对sentinel的支持 -feign: - sentinel: - enabled: true -# jwt 配置 -jwt: - config: - enabled: true - key-location: jwt.jks - key-alias: jwt - key-pass: 123456 - iss: youlai.tech - sub: all - access-exp-days: 30 \ No newline at end of file diff --git a/youlai-common/common-core/src/main/java/com/youlai/common/constant/AuthConstants.java b/youlai-common/common-core/src/main/java/com/youlai/common/constant/AuthConstants.java index a7b8fb165..22bfd21ce 100644 --- a/youlai-common/common-core/src/main/java/com/youlai/common/constant/AuthConstants.java +++ b/youlai-common/common-core/src/main/java/com/youlai/common/constant/AuthConstants.java @@ -2,7 +2,6 @@ package com.youlai.common.constant; public interface AuthConstants { - /** * 认证请求头key */ diff --git a/youlai-common/common-web/src/main/java/com/youlai/common/web/util/JwtUtils.java b/youlai-common/common-web/src/main/java/com/youlai/common/web/util/JwtUtils.java index c4bba4d66..dcc31ab7c 100644 --- a/youlai-common/common-web/src/main/java/com/youlai/common/web/util/JwtUtils.java +++ b/youlai-common/common-web/src/main/java/com/youlai/common/web/util/JwtUtils.java @@ -10,26 +10,40 @@ import org.apache.logging.log4j.util.Strings; import org.springframework.web.context.request.RequestContextHolder; import org.springframework.web.context.request.ServletRequestAttributes; import sun.misc.BASE64Decoder; - import javax.servlet.http.HttpServletRequest; +import java.net.URLDecoder; import java.util.List; -import java.util.stream.Collectors; +/** + * JWT工具类 + * + * @author xianrui + */ @Slf4j public class JwtUtils { + @SneakyThrows public static JSONObject getJwtPayload() { - String jwtPayload = ((ServletRequestAttributes) RequestContextHolder.getRequestAttributes()).getRequest().getHeader(AuthConstants.JWT_PAYLOAD_KEY); - JSONObject jsonObject = JSONUtil.parseObj(jwtPayload); + String payload = ((ServletRequestAttributes) RequestContextHolder.getRequestAttributes()).getRequest().getHeader(AuthConstants.JWT_PAYLOAD_KEY); + JSONObject jsonObject = JSONUtil.parseObj(URLDecoder.decode(payload,"UTF-8")); return jsonObject; } + /** + * 解析JWT获取用户ID + * + * @return + */ public static Long getUserId() { Long id = getJwtPayload().getLong(AuthConstants.USER_ID_KEY); return id; } - + /** + * 解析JWT获取获取用户名 + * + * @return + */ public static String getUsername() { String username = getJwtPayload().getStr(AuthConstants.USER_NAME_KEY); return username; @@ -37,7 +51,7 @@ public class JwtUtils { /** * 获取登录认证的客户端ID - *

+ * * 兼容两种方式获取Oauth2客户端信息(client_id、client_secret) * 方式一:client_id、client_secret放在请求路径中 * 方式二:放在请求头(Request Headers)中的Authorization字段,且经过加密,例如 Basic Y2xpZW50OnNlY3JldA== 明文等于 client:secret @@ -45,7 +59,7 @@ public class JwtUtils { * @return */ @SneakyThrows - public static String getAuthClientId() { + public static String getOAuthClientId() { String clientId; HttpServletRequest request = ((ServletRequestAttributes) RequestContextHolder.getRequestAttributes()).getRequest(); @@ -66,15 +80,17 @@ public class JwtUtils { return clientId; } + /** + * JWT获取用户角色列表 + * + * @return 角色列表 + */ public static List getRoles() { + List roles = null; JSONObject payload = getJwtPayload(); - if (payload != null && payload.size() > 0) { - List list = payload.get(AuthConstants.JWT_AUTHORITIES_KEY, List.class); - List roles = list.stream().collect(Collectors.toList()); - return roles; + if (payload != null && payload.containsKey(AuthConstants.JWT_AUTHORITIES_KEY)) { + roles = payload.get(AuthConstants.JWT_AUTHORITIES_KEY, List.class); } - return null; + return roles; } - - } diff --git a/youlai-gateway/src/main/java/com/youlai/gateway/component/RedisChannelListener.java b/youlai-gateway/src/main/java/com/youlai/gateway/component/RedisChannelListener.java index c9e9017bc..b21a19058 100644 --- a/youlai-gateway/src/main/java/com/youlai/gateway/component/RedisChannelListener.java +++ b/youlai-gateway/src/main/java/com/youlai/gateway/component/RedisChannelListener.java @@ -1,6 +1,5 @@ package com.youlai.gateway.component; -import cn.hutool.core.util.StrUtil; import com.youlai.common.constant.GlobalConstants; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.data.redis.connection.Message; @@ -11,13 +10,13 @@ import java.nio.charset.StandardCharsets; public class RedisChannelListener implements MessageListener { @Autowired - private AdminRoleLocalCache adminRoleLocalCache; + private UrlPermRolesLocalCache urlPermRolesLocalCache; @Override public void onMessage(Message message, byte[] bytes) { String msg = new String(message.getBody(), StandardCharsets.UTF_8); String channel = new String(message.getChannel(), StandardCharsets.UTF_8); - adminRoleLocalCache.remove(GlobalConstants.URL_PERM_ROLES_KEY); + urlPermRolesLocalCache.remove(GlobalConstants.URL_PERM_ROLES_KEY); } } diff --git a/youlai-gateway/src/main/java/com/youlai/gateway/component/AdminRoleLocalCache.java b/youlai-gateway/src/main/java/com/youlai/gateway/component/UrlPermRolesLocalCache.java similarity index 94% rename from youlai-gateway/src/main/java/com/youlai/gateway/component/AdminRoleLocalCache.java rename to youlai-gateway/src/main/java/com/youlai/gateway/component/UrlPermRolesLocalCache.java index 3724847f2..59a4e16b8 100644 --- a/youlai-gateway/src/main/java/com/youlai/gateway/component/AdminRoleLocalCache.java +++ b/youlai-gateway/src/main/java/com/youlai/gateway/component/UrlPermRolesLocalCache.java @@ -6,7 +6,6 @@ import lombok.extern.slf4j.Slf4j; import org.springframework.stereotype.Component; import javax.annotation.PostConstruct; -import java.util.Map; import java.util.concurrent.TimeUnit; /** @@ -17,7 +16,7 @@ import java.util.concurrent.TimeUnit; */ @Slf4j @Component -public class AdminRoleLocalCache { +public class UrlPermRolesLocalCache { private Cache localCache = null; @PostConstruct diff --git a/youlai-gateway/src/main/java/com/youlai/gateway/security/ResourceServerManager.java b/youlai-gateway/src/main/java/com/youlai/gateway/security/ResourceServerManager.java index d31ab1214..cac44c2c2 100644 --- a/youlai-gateway/src/main/java/com/youlai/gateway/security/ResourceServerManager.java +++ b/youlai-gateway/src/main/java/com/youlai/gateway/security/ResourceServerManager.java @@ -3,13 +3,11 @@ package com.youlai.gateway.security; import cn.hutool.core.collection.CollectionUtil; import cn.hutool.core.convert.Convert; import cn.hutool.core.util.StrUtil; -import cn.hutool.json.JSON; -import cn.hutool.json.JSONObject; -import cn.hutool.json.JSONUtil; import com.youlai.common.constant.AuthConstants; import com.youlai.common.constant.GlobalConstants; -import com.youlai.gateway.component.AdminRoleLocalCache; +import com.youlai.gateway.component.UrlPermRolesLocalCache; import lombok.AllArgsConstructor; +import lombok.RequiredArgsConstructor; import lombok.extern.slf4j.Slf4j; import org.springframework.beans.factory.annotation.Value; import org.springframework.data.redis.core.RedisTemplate; @@ -25,27 +23,29 @@ import org.springframework.util.AntPathMatcher; import org.springframework.util.PathMatcher; import reactor.core.publisher.Mono; +import java.util.ArrayList; import java.util.List; import java.util.Map; -import java.util.Set; /** * 网关自定义鉴权管理器 * - * @author haoxr - * @date 2020-05-01 + * @author xianrui */ @Component -@AllArgsConstructor +@RequiredArgsConstructor @Slf4j public class ResourceServerManager implements ReactiveAuthorizationManager { - private RedisTemplate redisTemplate; - private AdminRoleLocalCache adminRoleLocalCache; + private final RedisTemplate redisTemplate; + + // 本地缓存 + private final UrlPermRolesLocalCache urlPermRolesLocalCache; // 是否演示环境 - @Value("${demo}") - private Boolean isDemoEnv; + @Value("${local-cache.enabled}") + private Boolean localCacheEnabled; + @Override public Mono check(Mono mono, AuthorizationContext authorizationContext) { ServerHttpRequest request = authorizationContext.getExchange().getRequest(); @@ -53,66 +53,68 @@ public class ResourceServerManager implements ReactiveAuthorizationManager permRolesRules = (Map) adminRoleLocalCache.getCache(GlobalConstants.URL_PERM_ROLES_KEY); - if (isDemoEnv){ - // 缓存取【URL权限标识->角色集合】权限规则 - if(null==permRolesRules){ - permRolesRules = redisTemplate.opsForHash().entries(GlobalConstants.URL_PERM_ROLES_KEY); - adminRoleLocalCache.setLocalCache(GlobalConstants.URL_PERM_ROLES_KEY,permRolesRules); + // 缓存取 URL权限-角色集合 规则数据 + // urlPermRolesRules = [{'key':'GET:/api/v1/users/*','value':['ADMIN','TEST']},...] + Map urlPermRolesRules; + if (localCacheEnabled) { + urlPermRolesRules = (Map) urlPermRolesLocalCache.getCache(GlobalConstants.URL_PERM_ROLES_KEY); + if (null == urlPermRolesRules) { + urlPermRolesRules = redisTemplate.opsForHash().entries(GlobalConstants.URL_PERM_ROLES_KEY); + urlPermRolesLocalCache.setLocalCache(GlobalConstants.URL_PERM_ROLES_KEY, urlPermRolesRules); } + } else { + urlPermRolesRules = redisTemplate.opsForHash().entries(GlobalConstants.URL_PERM_ROLES_KEY); } - // 根据 “请求路径” 和 权限规则中的“URL权限标识”进行Ant匹配,得出拥有权限的角色集合 - Set hasPermissionRoles = CollectionUtil.newHashSet(); // 【声明定义】有权限的角色集合 - boolean needToCheck = false; // 【声明定义】是否需要被拦截检查的请求,如果缓存中权限规则中没有任何URL权限标识和此次请求的URL匹配,默认不需要被鉴权 - for (Map.Entry permRoles : permRolesRules.entrySet()) { - String perm = permRoles.getKey(); // 缓存权限规则的键:URL权限标识 + // 根据请求路径判断有访问权限的角色列表 + List authorizedRoles = new ArrayList<>(); // 拥有访问权限的角色 + boolean requireCheck = false; // 是否需要鉴权,默认“没有设置权限规则”不用鉴权 + + for (Map.Entry permRoles : urlPermRolesRules.entrySet()) { + String perm = permRoles.getKey(); if (pathMatcher.match(perm, restfulPath)) { - List roles = Convert.toList(String.class, permRoles.getValue()); // 缓存权限规则的值:有请求路径访问权限的角色集合 - hasPermissionRoles.addAll(Convert.toList(String.class, roles)); - if (needToCheck == false) { - needToCheck = true; + List roles = Convert.toList(String.class, permRoles.getValue()); + authorizedRoles.addAll(Convert.toList(String.class, roles)); + if (requireCheck == false) { + requireCheck = true; } } } - log.info("拥有接口访问权限的角色:{}", hasPermissionRoles.toString()); - // 没有设置权限规则放行;注:如果默认想拦截所有的请求请移除needToCheck变量逻辑即可,根据需求定制 - if (needToCheck == false) { + if (requireCheck == false) { return Mono.just(new AuthorizationDecision(true)); } - // 判断用户JWT中携带的角色是否有能通过权限拦截的角色 + // 判断JWT中携带的用户角色是否有权限访问 Mono authorizationDecisionMono = mono .filter(Authentication::isAuthenticated) .flatMapIterable(Authentication::getAuthorities) .map(GrantedAuthority::getAuthority) .any(authority -> { - log.info("用户权限 : {}", authority); // ROLE_ROOT - String role = authority.substring(AuthConstants.AUTHORITY_PREFIX.length()); // 角色编码:ROOT - if (GlobalConstants.ROOT_ROLE_CODE.equals(role)) { // 如果是超级管理员则放行 - return true; + String roleCode = authority.substring(AuthConstants.AUTHORITY_PREFIX.length()); // 用户的角色 + if (GlobalConstants.ROOT_ROLE_CODE.equals(roleCode)) { + return true; // 如果是超级管理员则放行 } - boolean hasPermission = CollectionUtil.isNotEmpty(hasPermissionRoles) && hasPermissionRoles.contains(role); // 用户角色中只要有一个满足则通过权限校验 - return hasPermission; + boolean hasAuthorized = CollectionUtil.isNotEmpty(authorizedRoles) && authorizedRoles.contains(roleCode); + return hasAuthorized; }) .map(AuthorizationDecision::new) .defaultIfEmpty(new AuthorizationDecision(false)); diff --git a/youlai-gateway/src/main/java/com/youlai/gateway/security/SecurityGlobalFilter.java b/youlai-gateway/src/main/java/com/youlai/gateway/security/SecurityGlobalFilter.java index 953c3d4f6..357e75962 100644 --- a/youlai-gateway/src/main/java/com/youlai/gateway/security/SecurityGlobalFilter.java +++ b/youlai-gateway/src/main/java/com/youlai/gateway/security/SecurityGlobalFilter.java @@ -7,10 +7,10 @@ import com.nimbusds.jose.JWSObject; import com.youlai.common.constant.AuthConstants; import com.youlai.common.result.ResultCode; import com.youlai.gateway.util.ResponseUtils; +import lombok.RequiredArgsConstructor; import lombok.SneakyThrows; import lombok.extern.slf4j.Slf4j; import org.apache.logging.log4j.util.Strings; -import org.springframework.beans.factory.annotation.Autowired; import org.springframework.beans.factory.annotation.Value; import org.springframework.cloud.gateway.filter.GatewayFilterChain; import org.springframework.cloud.gateway.filter.GlobalFilter; @@ -23,22 +23,23 @@ import org.springframework.stereotype.Component; import org.springframework.web.server.ServerWebExchange; import reactor.core.publisher.Mono; +import java.net.URLEncoder; + /** * 安全拦截全局过滤器 * - * @author haoxr - * @date 2020-06-12 + * @author xianrui */ @Component @Slf4j +@RequiredArgsConstructor public class SecurityGlobalFilter implements GlobalFilter, Ordered { - @Autowired - private RedisTemplate redisTemplate; + private final RedisTemplate redisTemplate; - // 是否演示环境 - @Value("${demo}") - private Boolean isDemoEnv; + + @Value("${spring.profiles.active}") + private String env; @SneakyThrows @Override @@ -47,8 +48,8 @@ public class SecurityGlobalFilter implements GlobalFilter, Ordered { ServerHttpRequest request = exchange.getRequest(); ServerHttpResponse response = exchange.getResponse(); - // 演示环境禁止删除和修改 - if (isDemoEnv + // 线上演示环境禁止修改和删除 + if (env.equals("prod") && (HttpMethod.DELETE.toString().equals(request.getMethodValue()) // 删除方法 || HttpMethod.PUT.toString().equals(request.getMethodValue())) // 修改方法 ) { @@ -74,7 +75,7 @@ public class SecurityGlobalFilter implements GlobalFilter, Ordered { // 存在token且不是黑名单,request写入JWT的载体信息 request = exchange.getRequest().mutate() - .header(AuthConstants.JWT_PAYLOAD_KEY, payload) + .header(AuthConstants.JWT_PAYLOAD_KEY, URLEncoder.encode(payload,"UTF-8")) .build(); exchange = exchange.mutate().request(request).build(); return chain.filter(exchange); diff --git a/youlai-gateway/src/main/java/com/youlai/gateway/swagger/SwaggerHandler.java b/youlai-gateway/src/main/java/com/youlai/gateway/swagger/SwaggerHandler.java index 24099a7cd..1091eb1a4 100644 --- a/youlai-gateway/src/main/java/com/youlai/gateway/swagger/SwaggerHandler.java +++ b/youlai-gateway/src/main/java/com/youlai/gateway/swagger/SwaggerHandler.java @@ -11,7 +11,7 @@ import springfox.documentation.swagger.web.*; import java.util.Optional; /** - * @Author haoxr + * @author xianrui * @Date 2021-02-25 16:34 * @Version 1.0.0 * @ https://gitee.com/xiaoym/swagger-bootstrap-ui-demo/blob/master/knife4j-spring-cloud-gateway/service-doc/src/main/java/com/xiaominfo/swagger/service/doc/handler/SwaggerHandler.java diff --git a/youlai-gateway/src/main/java/com/youlai/gateway/swagger/SwaggerHeaderFilter.java b/youlai-gateway/src/main/java/com/youlai/gateway/swagger/SwaggerHeaderFilter.java index d2463cf1b..422e2509a 100644 --- a/youlai-gateway/src/main/java/com/youlai/gateway/swagger/SwaggerHeaderFilter.java +++ b/youlai-gateway/src/main/java/com/youlai/gateway/swagger/SwaggerHeaderFilter.java @@ -8,9 +8,9 @@ import org.springframework.stereotype.Component; import org.springframework.web.server.ServerWebExchange; /** - * @auth haoxr + * @link https://gitee.com/xiaoym/swagger-bootstrap-ui-demo/blob/master/knife4j-spring-cloud-gateway/service-doc/src/main/java/com/xiaominfo/swagger/service/doc/config/SwaggerHeaderFilter.java + * @auth xianrui * @date 2021-02-25 16:29 - * @link https://gitee.com/xiaoym/swagger-bootstrap-ui-demo/blob/master/knife4j-spring-cloud-gateway/service-doc/src/main/java/com/xiaominfo/swagger/service/doc/config/SwaggerHeaderFilter.java */ @Component public class SwaggerHeaderFilter extends AbstractGatewayFilterFactory { diff --git a/youlai-gateway/src/main/resources/bootstrap-dev.yml b/youlai-gateway/src/main/resources/bootstrap-dev.yml index 74118fdbd..e9889ab1d 100644 --- a/youlai-gateway/src/main/resources/bootstrap-dev.yml +++ b/youlai-gateway/src/main/resources/bootstrap-dev.yml @@ -13,29 +13,7 @@ spring: config: server-addr: ${spring.cloud.nacos.discovery.server-addr} file-extension: yaml - sentinel: - enabled: false # 网关流控开关 - eager: true # 取消控制台懒加载,项目启动即连接Sentinel - transport: - client-ip: localhost - dashboard: localhost:8080 - datasource: - # 网关限流规则,gw-flow为key,随便定义 - gw-flow: - nacos: - server-addr: ${spring.cloud.nacos.discovery.server-addr} - dataId: ${spring.application.name}-gw-flow-rules - groupId: SENTINEL_GROUP - data-type: json - rule-type: gw-flow - # 网关API自定义分组 - gw-api-group: - nacos: - server-addr: ${spring.cloud.nacos.discovery.server-addr} - dataId: ${spring.application.name}-gw-api-group-rules - groupId: SENTINEL_GROUP - data-type: json - rule-type: gw-api-group + diff --git a/youlai-gateway/src/main/resources/bootstrap-prod.yml b/youlai-gateway/src/main/resources/bootstrap-prod.yml index d759d91c4..f25fe2241 100644 --- a/youlai-gateway/src/main/resources/bootstrap-prod.yml +++ b/youlai-gateway/src/main/resources/bootstrap-prod.yml @@ -15,24 +15,3 @@ spring: server-addr: ${spring.cloud.nacos.discovery.server-addr} file-extension: yaml namespace: prod_namespace_id - sentinel: - eager: true - transport: - dashboard: e.youlai.tech:8858 - datasource: - # 网关限流 - gw-flow: - nacos: - server-addr: ${spring.cloud.nacos.discovery.server-addr} - dataId: ${spring.application.name}-gw-flow-rules - groupId: SENTINEL_GROUP - data-type: json - rule-type: gw-flow - # 网关API自定义分组 - gw-api-group: - nacos: - server-addr: ${spring.cloud.nacos.discovery.server-addr} - dataId: ${spring.application.name}-gw-api-group-rules - groupId: SENTINEL_GROUP - data-type: json - rule-type: gw-api-group