RuoYi Gateway 网关路由分发机制
RuoYi Gateway 网关路由分发机制
概述
RuoYi Gateway是基于Spring Cloud Gateway实现的微服务网关,通过读取Nacos配置中心的配置实现动态路由分发。本文档详细说明了网关如何通过Nacos配置实现路由转发、安全过滤和负载均衡等功能。
网关架构
核心组件
- Spring Cloud Gateway: 基于Spring WebFlux的响应式网关框架
- Nacos Discovery: 服务注册与发现组件
- Nacos Config: 配置中心组件
- Sentinel: 流量控制和熔断降级组件
- Redis: 缓存和会话存储
项目结构
ruoyi-gateway/
├── src/main/java/com/ruoyi/gateway/
│ ├── RuoYiGatewayApplication.java # 网关启动类
│ ├── config/ # 配置类
│ │ ├── properties/ # 配置属性类
│ │ │ ├── CaptchaProperties.java # 验证码配置属性
│ │ │ ├── IgnoreWhiteProperties.java # 白名单配置
│ │ │ └── XssProperties.java # XSS配置属性
│ │ ├── CaptchaConfig.java # 验证码配置
│ │ ├── GatewayConfig.java # 网关配置
│ │ ├── KaptchaTextCreator.java # 验证码生成器
│ │ ├── RouterFunctionConfiguration.java # 路由配置
│ │ └── SpringDocConfig.java # API文档配置
│ ├── filter/ # 过滤器
│ │ ├── AuthFilter.java # 认证过滤器
│ │ ├── BlackListUrlFilter.java # 黑名单过滤器
│ │ ├── ValidateCodeFilter.java # 验证码过滤器
│ │ └── XssFilter.java # XSS防护过滤器
│ ├── handler/ # 处理器
│ │ ├── GatewayExceptionHandler.java # 网关异常处理器
│ │ ├── SentinelFallbackHandler.java # Sentinel降级处理器
│ │ └── ValidateCodeHandler.java # 验证码处理器
│ └── service/ # 服务层
│ ├── ValidateCodeService.java # 验证码服务接口
│ └── impl/
│ └── ValidateCodeServiceImpl.java # 验证码服务实现
└── src/main/resources/
├── bootstrap.yml # 启动配置
├── logback.xml # 日志配置
└── banner.txt # 启动标语Nacos配置集成
1. bootstrap.yml 配置
网关通过bootstrap.yml文件配置与Nacos的连接:
# Tomcat
server:
port: 8080
# Spring
spring:
application:
# 应用名称
name: ruoyi-gateway
profiles:
# 环境配置
active: dev
cloud:
nacos:
discovery:
# 服务注册地址
server-addr: 192.168.0.133
config:
# 配置中心地址
server-addr: 192.168.0.133
# 配置文件格式
file-extension: yml
# 共享配置
shared-configs:
- application-${spring.profiles.active}.${spring.cloud.nacos.config.file-extension}
sentinel:
# 取消控制台懒加载
eager: true
transport:
# 控制台地址
dashboard: 127.0.0.1:8718
# nacos配置持久化
datasource:
ds1:
nacos:
server-addr: 192.168.0.133
dataId: sentinel-ruoyi-gateway
groupId: DEFAULT_GROUP
data-type: json
rule-type: gw-flow2. Nacos中的路由配置
在Nacos配置中心,网关路由配置存储在ruoyi-gateway-dev.yml配置文件中:
spring:
data:
redis:
host: 192.168.0.133
port: 6379
password:
cloud:
gateway:
discovery:
locator:
lowerCaseServiceId: true
enabled: true
routes:
# 认证中心
- id: ruoyi-auth
uri: lb://ruoyi-auth
predicates:
- Path=/auth/**
filters:
# 验证码处理
- CacheRequestBody
- ValidateCodeFilter
- StripPrefix=1
# 代码生成
- id: ruoyi-gen
uri: lb://ruoyi-gen
predicates:
- Path=/code/**
filters:
- StripPrefix=1
# 定时任务
- id: ruoyi-job
uri: lb://ruoyi-job
predicates:
- Path=/schedule/**
filters:
- StripPrefix=1
# 系统模块
- id: ruoyi-system
uri: lb://ruoyi-system
predicates:
- Path=/system/**
filters:
- StripPrefix=1
# 文件服务
- id: ruoyi-file
uri: lb://ruoyi-file
predicates:
- Path=/file/**
filters:
- StripPrefix=1
# 工作流中心
- id: ruoyi-flowable
uri: lb://ruoyi-flowable
predicates:
- Path=/flowable/**
filters:
- StripPrefix=1
# 安全配置
security:
# 验证码
captcha:
enabled: true
type: math
# 防止XSS攻击
xss:
enabled: true
excludeUrls:
- /system/notice
# 这个是新加的,不然保存流程的时候会被拦截
- /flowable/model/save
# 不校验白名单
ignore:
whites:
- /auth/logout
- /auth/login
- /auth/register
- /*/v2/api-docs
- /*/v3/api-docs
- /csrf
# springdoc配置
springdoc:
webjars:
# 访问前缀
prefix:路由分发机制
1. 路由发现
网关通过以下两种方式发现路由:
自动发现路由:
discovery: locator: lowerCaseServiceId: true # 将服务ID转为小写 enabled: true # 启用自动发现手动配置路由:
在配置文件中明确定义每个路由规则,包括ID、URI、谓词和过滤器。
2. 路由匹配流程
- 请求接收:网关接收到HTTP请求
- 路由匹配:根据请求路径和谓词条件匹配路由规则
- 过滤器链执行:按照顺序执行匹配路由的过滤器链
- 负载均衡:通过
lb://service-name格式实现负载均衡 - 请求转发:将处理后的请求转发到目标服务
3. 负载均衡
使用lb://服务名的URI格式,Spring Cloud Gateway会自动集成Spring Cloud LoadBalancer实现负载均衡:
- id: ruoyi-system
uri: lb://ruoyi-system # 使用负载均衡器
predicates:
- Path=/system/**
filters:
- StripPrefix=1 # 去除前缀安全过滤机制
1. 认证过滤器 (AuthFilter)
认证过滤器负责验证用户的JWT令牌:
@Component
public class AuthFilter implements GlobalFilter, Ordered {
private static final Logger log = LoggerFactory.getLogger(AuthFilter.class);
// 排除过滤的 uri 地址,nacos自行添加
@Autowired
private IgnoreWhiteProperties ignoreWhite;
@Autowired
private RedisService redisService;
@Override
public Mono<Void> filter(ServerWebExchange exchange, GatewayFilterChain chain) {
ServerHttpRequest request = exchange.getRequest();
ServerHttpRequest.Builder mutate = request.mutate();
String url = request.getURI().getPath();
// 跳过不需要验证的路径
if (StringUtils.matches(url, ignoreWhite.getWhites())) {
return chain.filter(exchange);
}
String token = getToken(request);
if (StringUtils.isEmpty(token)) {
return unauthorizedResponse(exchange, "令牌不能为空");
}
Claims claims = JwtUtils.parseToken(token);
if (claims == null) {
return unauthorizedResponse(exchange, "令牌已过期或验证不正确!");
}
String userkey = JwtUtils.getUserKey(claims);
boolean islogin = redisService.hasKey(getTokenKey(userkey));
if (!islogin) {
return unauthorizedResponse(exchange, "登录状态已过期");
}
String userid = JwtUtils.getUserId(claims);
String username = JwtUtils.getUserName(claims);
if (StringUtils.isEmpty(userid) || StringUtils.isEmpty(username)) {
return unauthorizedResponse(exchange, "令牌验证失败");
}
// 设置用户信息到请求
addHeader(mutate, SecurityConstants.USER_KEY, userkey);
addHeader(mutate, SecurityConstants.DETAILS_USER_ID, userid);
addHeader(mutate, SecurityConstants.DETAILS_USERNAME, username);
// 内部请求来源参数清除
removeHeader(mutate, SecurityConstants.FROM_SOURCE);
return chain.filter(exchange.mutate().request(mutate.build()).build());
}
// 其他方法...
}2. 验证码过滤器 (ValidateCodeFilter)
验证码过滤器用于登录和注册时的验证码校验:
public class ValidateCodeFilter extends AbstractGatewayFilterFactory<Object> {
private final static String[] VALIDATE_URL = new String[] { "/auth/login", "/auth/register" };
// 非登录/注册请求或验证码关闭,不处理
if (!StringUtils.equalsAnyIgnoreCase(request.getURI().getPath(), VALIDATE_URL) || !captchaProperties.getEnabled()) {
return chain.filter(exchange);
}
// 校验验证码
validateCodeService.checkCaptcha(obj.getString(CODE), obj.getString(UUID));
}3. XSS防护过滤器 (XssFilter)
XSS防护过滤器用于防止跨站脚本攻击:
@Component
@ConditionalOnProperty(value = "security.xss.enabled", havingValue = "true")
public class XssFilter implements GlobalFilter, Ordered {
// 排除路径不过滤
if (StringUtils.matches(url, xss.getExcludeUrls())) {
return chain.filter(exchange);
}
// 防xss攻击过滤
bodyStr = EscapeUtil.clean(bodyStr);
}动态配置更新
1. @RefreshScope 注解
配置类使用@RefreshScope注解实现配置的动态更新:
@Configuration
@RefreshScope
@ConfigurationProperties(prefix = "security.ignore")
public class IgnoreWhiteProperties {
private List<String> whites = new ArrayList<>();
}2. Nacos配置监听
当Nacos中的配置发生变化时,网关会自动接收更新并刷新配置,无需重启服务。
限流配置
网关使用Sentinel实现限流功能,限流规则存储在Nacos配置中心:
[
{
"resource": "ruoyi-auth",
"count": 500,
"grade": 1,
"limitApp": "default",
"strategy": 0,
"controlBehavior": 0
},
{
"resource": "ruoyi-system",
"count": 1000,
"grade": 1,
"limitApp": "default",
"strategy": 0,
"controlBehavior": 0
}
]Sentinel集成
网关配置了Sentinel进行流量控制和熔断降级:
spring:
cloud:
sentinel:
# 取消控制台懒加载
eager: true
transport:
# 控制台地址
dashboard: 127.0.0.1:8718
# nacos配置持久化
datasource:
ds1:
nacos:
server-addr: 192.168.0.133
dataId: sentinel-ruoyi-gateway
groupId: DEFAULT_GROUP
data-type: json
rule-type: gw-flow工作流程总结
启动阶段:
- 网关服务启动,从Nacos配置中心读取配置
- 向Nacos注册中心注册服务
- 初始化路由规则和过滤器链
- 初始化Sentinel配置
请求处理阶段:
- 接收客户端请求
- 根据请求路径匹配路由规则
- 执行过滤器链(认证、验证码、XSS防护等)
- 通过Sentinel进行流量控制
- 通过负载均衡选择目标服务实例
- 转发请求并获取响应
- 返回响应给客户端
配置更新阶段:
- Nacos配置变更时推送更新
- 网关接收配置变更通知
- 刷新路由规则和过滤器配置
- 刷新Sentinel规则
- 无需重启即可应用新配置
总结
RuoYi Gateway通过Nacos配置中心实现了灵活的动态路由管理,结合Spring Cloud Gateway的响应式架构和多种过滤器机制,提供了高性能、高可用的微服务网关解决方案。通过集中式配置管理,实现了路由规则、安全策略和限流配置的动态更新,大大提高了系统的可维护性和灵活性。
网关集成了Sentinel进行流量控制和熔断降级,保障了系统在高并发情况下的稳定性。同时,网关提供了完善的安全过滤机制,包括认证授权、验证码校验、XSS防护等,确保了系统的安全性。