diff --git a/novalon-manage-api/manage-gateway/pom.xml b/novalon-manage-api/manage-gateway/pom.xml
index 169ea14..0dd4c48 100644
--- a/novalon-manage-api/manage-gateway/pom.xml
+++ b/novalon-manage-api/manage-gateway/pom.xml
@@ -28,7 +28,33 @@
org.springframework.cloud
spring-cloud-starter-gateway
- 4.1.0
+
+
+ io.jsonwebtoken
+ jjwt-api
+ 0.11.5
+
+
+ io.jsonwebtoken
+ jjwt-impl
+ 0.11.5
+ runtime
+
+
+ io.jsonwebtoken
+ jjwt-jackson
+ 0.11.5
+ runtime
+
+
+ io.github.resilience4j
+ resilience4j-spring-boot3
+ 2.2.0
+
+
+ io.github.resilience4j
+ resilience4j-reactor
+ 2.2.0
io.micrometer
diff --git a/novalon-manage-api/manage-gateway/src/main/java/cn/novalon/manage/gateway/config/RateLimitConfig.java b/novalon-manage-api/manage-gateway/src/main/java/cn/novalon/manage/gateway/config/RateLimitConfig.java
new file mode 100644
index 0000000..7786df8
--- /dev/null
+++ b/novalon-manage-api/manage-gateway/src/main/java/cn/novalon/manage/gateway/config/RateLimitConfig.java
@@ -0,0 +1,49 @@
+package cn.novalon.manage.gateway.config;
+
+import io.github.resilience4j.ratelimiter.RateLimiter;
+import io.github.resilience4j.ratelimiter.RateLimiterConfig;
+import io.github.resilience4j.ratelimiter.RateLimiterRegistry;
+import org.springframework.beans.factory.annotation.Value;
+import org.springframework.context.annotation.Bean;
+import org.springframework.context.annotation.Configuration;
+
+import java.time.Duration;
+
+/**
+ * 限流配置类
+ *
+ * 文件定义:配置API限流策略,使用Resilience4j实现
+ * 涉及业务:API访问频率控制,防止滥用和DDoS攻击
+ * 算法:使用Resilience4j的RateLimiter实现令牌桶算法
+ *
+ * @author 张翔
+ * @date 2026-03-13
+ */
+@Configuration
+public class RateLimitConfig {
+
+ @Value("${rate.limit.limit-for-period:100}")
+ private int limitForPeriod;
+
+ @Value("${rate.limit.limit-refresh-period:1s}")
+ private Duration limitRefreshPeriod;
+
+ @Value("${rate.limit.timeout-duration:0}")
+ private Duration timeoutDuration;
+
+ @Bean
+ public RateLimiterRegistry rateLimiterRegistry() {
+ RateLimiterConfig config = RateLimiterConfig.custom()
+ .limitForPeriod(limitForPeriod)
+ .limitRefreshPeriod(limitRefreshPeriod)
+ .timeoutDuration(timeoutDuration)
+ .build();
+
+ return RateLimiterRegistry.of(config);
+ }
+
+ @Bean
+ public RateLimiter apiRateLimiter(RateLimiterRegistry registry) {
+ return registry.rateLimiter("apiRateLimiter");
+ }
+}
\ No newline at end of file
diff --git a/novalon-manage-api/manage-gateway/src/main/java/cn/novalon/manage/gateway/filter/JwtAuthenticationFilter.java b/novalon-manage-api/manage-gateway/src/main/java/cn/novalon/manage/gateway/filter/JwtAuthenticationFilter.java
new file mode 100644
index 0000000..f2730c9
--- /dev/null
+++ b/novalon-manage-api/manage-gateway/src/main/java/cn/novalon/manage/gateway/filter/JwtAuthenticationFilter.java
@@ -0,0 +1,66 @@
+package cn.novalon.manage.gateway.filter;
+
+import cn.novalon.manage.gateway.util.JwtUtil;
+import org.springframework.cloud.gateway.filter.GatewayFilter;
+import org.springframework.cloud.gateway.filter.factory.AbstractGatewayFilterFactory;
+import org.springframework.http.HttpHeaders;
+import org.springframework.http.HttpStatus;
+import org.springframework.http.server.reactive.ServerHttpRequest;
+import org.springframework.stereotype.Component;
+import org.springframework.web.server.ServerWebExchange;
+
+@Component
+public class JwtAuthenticationFilter extends AbstractGatewayFilterFactory {
+
+ private final JwtUtil jwtUtil;
+
+ public JwtAuthenticationFilter(JwtUtil jwtUtil) {
+ super(Config.class);
+ this.jwtUtil = jwtUtil;
+ }
+
+ @Override
+ public GatewayFilter apply(Config config) {
+ return (exchange, chain) -> {
+ ServerHttpRequest request = exchange.getRequest();
+ String path = request.getURI().getPath();
+
+ if (isPublicPath(path)) {
+ return chain.filter(exchange);
+ }
+
+ String authHeader = request.getHeaders().getFirst(HttpHeaders.AUTHORIZATION);
+
+ if (authHeader == null || !authHeader.startsWith("Bearer ")) {
+ exchange.getResponse().setStatusCode(HttpStatus.UNAUTHORIZED);
+ return exchange.getResponse().setComplete();
+ }
+
+ String token = authHeader.substring(7);
+
+ if (!jwtUtil.validateToken(token) || jwtUtil.isTokenExpired(token)) {
+ exchange.getResponse().setStatusCode(HttpStatus.UNAUTHORIZED);
+ return exchange.getResponse().setComplete();
+ }
+
+ String username = jwtUtil.getUsernameFromToken(token);
+ Long userId = jwtUtil.getUserIdFromToken(token);
+
+ ServerHttpRequest modifiedRequest = request.mutate()
+ .header("X-User-Id", String.valueOf(userId))
+ .header("X-Username", username)
+ .build();
+
+ return chain.filter(exchange.mutate().request(modifiedRequest).build());
+ };
+ }
+
+ private boolean isPublicPath(String path) {
+ return path.startsWith("/api/auth/") ||
+ path.equals("/actuator/health") ||
+ path.startsWith("/actuator/info");
+ }
+
+ public static class Config {
+ }
+}
diff --git a/novalon-manage-api/manage-gateway/src/main/java/cn/novalon/manage/gateway/filter/RbacAuthorizationFilter.java b/novalon-manage-api/manage-gateway/src/main/java/cn/novalon/manage/gateway/filter/RbacAuthorizationFilter.java
new file mode 100644
index 0000000..0c715f7
--- /dev/null
+++ b/novalon-manage-api/manage-gateway/src/main/java/cn/novalon/manage/gateway/filter/RbacAuthorizationFilter.java
@@ -0,0 +1,58 @@
+package cn.novalon.manage.gateway.filter;
+
+import org.springframework.cloud.gateway.filter.GatewayFilter;
+import org.springframework.cloud.gateway.filter.factory.AbstractGatewayFilterFactory;
+import org.springframework.http.HttpStatus;
+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.util.List;
+
+@Component
+public class RbacAuthorizationFilter extends AbstractGatewayFilterFactory {
+
+ public RbacAuthorizationFilter() {
+ super(Config.class);
+ }
+
+ @Override
+ public GatewayFilter apply(Config config) {
+ return (exchange, chain) -> {
+ ServerHttpRequest request = exchange.getRequest();
+ String path = request.getURI().getPath();
+ String method = request.getMethod().name();
+
+ if (isPublicPath(path)) {
+ return chain.filter(exchange);
+ }
+
+ String userIdHeader = request.getHeaders().getFirst("X-User-Id");
+ if (userIdHeader == null) {
+ exchange.getResponse().setStatusCode(HttpStatus.UNAUTHORIZED);
+ return exchange.getResponse().setComplete();
+ }
+
+ if (!hasPermission(path, method)) {
+ exchange.getResponse().setStatusCode(HttpStatus.FORBIDDEN);
+ return exchange.getResponse().setComplete();
+ }
+
+ return chain.filter(exchange);
+ };
+ }
+
+ private boolean isPublicPath(String path) {
+ return path.startsWith("/api/auth/") ||
+ path.equals("/actuator/health") ||
+ path.startsWith("/actuator/info");
+ }
+
+ private boolean hasPermission(String path, String method) {
+ return true;
+ }
+
+ public static class Config {
+ }
+}
diff --git a/novalon-manage-api/manage-gateway/src/main/java/cn/novalon/manage/gateway/util/JwtUtil.java b/novalon-manage-api/manage-gateway/src/main/java/cn/novalon/manage/gateway/util/JwtUtil.java
new file mode 100644
index 0000000..8614be4
--- /dev/null
+++ b/novalon-manage-api/manage-gateway/src/main/java/cn/novalon/manage/gateway/util/JwtUtil.java
@@ -0,0 +1,74 @@
+package cn.novalon.manage.gateway.util;
+
+import io.jsonwebtoken.Claims;
+import io.jsonwebtoken.Jwts;
+import io.jsonwebtoken.security.Keys;
+import org.springframework.beans.factory.annotation.Value;
+import org.springframework.stereotype.Component;
+
+import javax.crypto.SecretKey;
+import java.nio.charset.StandardCharsets;
+import java.util.Date;
+
+@Component
+public class JwtUtil {
+
+ @Value("${jwt.secret:mySecretKey}")
+ private String secret;
+
+ @Value("${jwt.expiration:86400000}")
+ private Long expiration;
+
+ private SecretKey getSigningKey() {
+ return Keys.hmacShaKeyFor(secret.getBytes(StandardCharsets.UTF_8));
+ }
+
+ public String generateToken(String username, Long userId) {
+ Date now = new Date();
+ Date expiryDate = new Date(now.getTime() + expiration);
+
+ return Jwts.builder()
+ .setSubject(username)
+ .claim("userId", userId)
+ .setIssuedAt(now)
+ .setExpiration(expiryDate)
+ .signWith(getSigningKey())
+ .compact();
+ }
+
+ public Claims parseToken(String token) {
+ return Jwts.parserBuilder()
+ .setSigningKey(getSigningKey())
+ .build()
+ .parseClaimsJws(token)
+ .getBody();
+ }
+
+ public String getUsernameFromToken(String token) {
+ Claims claims = parseToken(token);
+ return claims.getSubject();
+ }
+
+ public Long getUserIdFromToken(String token) {
+ Claims claims = parseToken(token);
+ return claims.get("userId", Long.class);
+ }
+
+ public boolean validateToken(String token) {
+ try {
+ parseToken(token);
+ return true;
+ } catch (Exception e) {
+ return false;
+ }
+ }
+
+ public boolean isTokenExpired(String token) {
+ try {
+ Claims claims = parseToken(token);
+ return claims.getExpiration().before(new Date());
+ } catch (Exception e) {
+ return true;
+ }
+ }
+}
diff --git a/novalon-manage-api/manage-gateway/src/main/resources/application.yml b/novalon-manage-api/manage-gateway/src/main/resources/application.yml
index 43a1fd8..03e1c68 100644
--- a/novalon-manage-api/manage-gateway/src/main/resources/application.yml
+++ b/novalon-manage-api/manage-gateway/src/main/resources/application.yml
@@ -12,6 +12,8 @@ spring:
predicates:
- Path=/api/**
default-filters:
+ - name: JwtAuthentication
+ - name: RbacAuthorization
- name: Retry
args:
retries: 3
@@ -23,6 +25,10 @@ spring:
factor: 2
basedOnPreviousValue: false
+jwt:
+ secret: ${JWT_SECRET:mySecretKeyForNovalonManageSystem2024}
+ expiration: ${JWT_EXPIRATION:86400000}
+
management:
endpoints:
web: