淘宝客app的API网关设计:认证授权与流量控制策略
大家好,我是阿可,微赚淘客系统及省赚客APP创始人,是个冬天不穿秋裤,天冷也要风度的程序猿!
在淘宝客app的微服务架构中,随着商品服务、订单服务、返利服务等10+微服务的拆分,前端调用面临接口分散、认证逻辑重复、流量难以管控等问题------比如每个服务都要单独实现用户登录校验,大促期间商品查询接口易因流量突增崩溃。基于此,我们引入Spring Cloud Gateway作为API网关 ,将认证授权、流量控制、请求路由等共性能力收敛到网关层,实现"前端-网关-微服务"的统一接入架构。以下从网关整体设计、认证授权实现、流量控制策略三方面展开,附完整代码示例。
一、淘宝客app API网关整体架构
1.1 网关核心职责与技术选型
API网关作为淘宝客app的"流量入口",承担四大核心职责:
- 请求路由 :根据URL路径将请求转发至对应微服务(如
/api/product/**
转发至商品服务); - 认证授权:统一校验用户Token,拦截未登录请求,校验接口访问权限;
- 流量控制:对高频接口(如商品列表查询)进行限流、熔断,保护后端服务;
- 监控日志:记录请求耗时、响应状态,便于问题排查与链路追踪。
技术选型上,采用Spring Cloud Gateway(非阻塞异步架构,性能优于Zuul),搭配Redis实现分布式限流,结合JWT实现Token认证,架构图如下(文字描述):前端请求→API网关(认证/限流/路由)→微服务集群→网关聚合响应→返回前端。
1.2 网关基础配置(application.yml)
通过配置文件定义路由规则、全局过滤器、Redis限流数据源,代码如下:
yaml
spring:
cloud:
gateway:
# 路由规则配置
routes:
# 商品服务路由
- id: product-service-route
uri: lb://product-service # 负载均衡到商品服务(服务名从Nacos获取)
predicates:
- Path=/api/product/** # 匹配路径
filters:
- StripPrefix=1 # 去除路径前缀(/api/product/xxx → /product/xxx)
- name: RequestRateLimiter # 限流过滤器
args:
redis-rate-limiter.replenishRate: 100 # 令牌桶每秒填充速率
redis-rate-limiter.burstCapacity: 200 # 令牌桶最大容量
key-resolver: "#{@userKeyResolver}" # 限流键解析器(用户维度)
- name: AuthFilter # 自定义认证过滤器
args:
ignorePaths: /api/product/info # 无需认证的接口(如商品详情)
# 订单服务路由
- id: order-service-route
uri: lb://order-service
predicates:
- Path=/api/order/**
filters:
- StripPrefix=1
- name: RequestRateLimiter
args:
redis-rate-limiter.replenishRate: 50
redis-rate-limiter.burstCapacity: 100
key-resolver: "#{@userKeyResolver}"
# Redis配置(用于限流与Token缓存)
redis:
host: redis-service
port: 6379
password: TaokeRedis@2024
lettuce:
pool:
max-active: 16
max-idle: 8
# 网关服务端口
server:
port: 8080
# 日志配置(记录请求详情)
logging:
level:
org.springframework.cloud.gateway: info
cn.juwatech.taoke.gateway: debug
二、认证授权核心实现
2.1 JWT Token生成与校验工具类
封装JWT的创建、解析、签名验证逻辑,代码如下:
java
package cn.juwatech.taoke.gateway.utils;
import io.jsonwebtoken.Claims;
import io.jsonwebtoken.Jwts;
import io.jsonwebtoken.SignatureAlgorithm;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Component;
import java.util.Date;
import java.util.HashMap;
import java.util.Map;
/**
* JWT工具类(生成Token、解析Token、验证Token)
*/
@Component
public class JwtUtils {
// 密钥(从配置中心获取,避免硬编码)
@Value("${taoke.jwt.secret:juwatech_taoke_secret_2024}")
private String secret;
// Token有效期(2小时,单位:毫秒)
@Value("${taoke.jwt.expire:7200000}")
private long expire;
/**
* 生成用户Token(包含用户ID、用户名、角色)
*/
public String generateToken(String userId, String username, String role) {
// Token过期时间
Date expireDate = new Date(System.currentTimeMillis() + expire);
// 自定义Claims(携带业务信息)
Map<String, Object> claims = new HashMap<>();
claims.put("userId", userId);
claims.put("username", username);
claims.put("role", role);
return Jwts.builder()
.setClaims(claims)
.setExpiration(expireDate)
.signWith(SignatureAlgorithm.HS256, secret) // HS256签名算法
.compact();
}
/**
* 解析Token,获取Claims
*/
public Claims parseToken(String token) {
try {
return Jwts.parser()
.setSigningKey(secret)
.parseClaimsJws(token)
.getBody();
} catch (Exception e) {
throw new RuntimeException("Token解析失败:" + e.getMessage());
}
}
/**
* 验证Token有效性(是否过期、签名是否正确)
*/
public boolean validateToken(String token) {
try {
Claims claims = parseToken(token);
// 检查Token是否过期
return !claims.getExpiration().before(new Date());
} catch (Exception e) {
return false;
}
}
/**
* 从Token中获取用户ID
*/
public String getUserIdFromToken(String token) {
Claims claims = parseToken(token);
return claims.get("userId", String.class);
}
/**
* 从Token中获取用户角色
*/
public String getRoleFromToken(String token) {
Claims claims = parseToken(token);
return claims.get("role", String.class);
}
}
2.2 自定义认证过滤器(AuthFilter)
实现GlobalFilter接口,在网关层统一拦截请求,校验Token与接口权限,代码如下:
java
package cn.juwatech.taoke.gateway.filter;
import cn.juwatech.taoke.gateway.utils.JwtUtils;
import org.springframework.cloud.gateway.filter.GlobalFilter;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.core.annotation.Order;
import org.springframework.http.HttpStatus;
import org.springframework.http.server.reactive.ServerHttpRequest;
import org.springframework.http.server.reactive.ServerHttpResponse;
import org.springframework.util.AntPathMatcher;
import org.springframework.util.StringUtils;
import org.springframework.web.server.ServerWebExchange;
import reactor.core.publisher.Mono;
import javax.annotation.Resource;
import java.util.List;
/**
* 认证授权全局过滤器(Order=1,优先执行)
*/
@Configuration
public class AuthFilterConfig {
// 路径匹配器(用于判断是否忽略认证)
private final AntPathMatcher pathMatcher = new AntPathMatcher();
// 注入JWT工具类
@Resource
private JwtUtils jwtUtils;
@Bean
@Order(1)
public GlobalFilter authFilter() {
return (exchange, chain) -> {
ServerHttpRequest request = exchange.getRequest();
ServerHttpResponse response = exchange.getResponse();
String requestPath = request.getPath().value();
// 1. 获取无需认证的路径列表(从路由过滤器参数中获取)
List<String> ignorePaths = exchange.getAttribute("ignorePaths");
if (ignorePaths != null && ignorePaths.stream().anyMatch(path -> pathMatcher.match(path, requestPath))) {
// 无需认证,直接放行
return chain.filter(exchange);
}
// 2. 从请求头获取Token(格式:Bearer {token})
String authHeader = request.getHeaders().getFirst("Authorization");
if (!StringUtils.hasText(authHeader) || !authHeader.startsWith("Bearer ")) {
// Token不存在或格式错误,返回401未授权
response.setStatusCode(HttpStatus.UNAUTHORIZED);
return response.writeWith(Mono.just(response.bufferFactory().wrap("请先登录".getBytes())));
}
String token = authHeader.substring(7);
// 3. 验证Token有效性
if (!jwtUtils.validateToken(token)) {
response.setStatusCode(HttpStatus.UNAUTHORIZED);
return response.writeWith(Mono.just(response.bufferFactory().wrap("Token已过期或无效".getBytes())));
}
// 4. 校验接口访问权限(示例:管理员角色才能访问订单管理接口)
String role = jwtUtils.getRoleFromToken(token);
if (requestPath.startsWith("/api/order/admin/") && !"ADMIN".equals(role)) {
response.setStatusCode(HttpStatus.FORBIDDEN);
return response.writeWith(Mono.just(response.bufferFactory().wrap("无权限访问".getBytes())));
}
// 5. Token有效且有权限,将用户信息存入请求头,传递给后端服务
String userId = jwtUtils.getUserIdFromToken(token);
ServerHttpRequest newRequest = request.mutate()
.header("X-User-Id", userId)
.header("X-User-Role", role)
.build();
return chain.filter(exchange.mutate().request(newRequest).build());
};
}
}
三、流量控制策略实现
3.1 分布式限流键解析器
基于用户ID实现分布式限流(不同用户独立计数),避免单用户高频请求影响其他用户,代码如下:
java
package cn.juwatech.taoke.gateway.config;
import org.springframework.cloud.gateway.filter.ratelimit.KeyResolver;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import reactor.core.publisher.Mono;
/**
* 限流键解析器配置(用户维度限流)
*/
@Configuration
public class RateLimiterConfig {
/**
* 用户维度限流键解析器(从Token或请求参数获取用户ID)
*/
@Bean("userKeyResolver")
public KeyResolver userKeyResolver() {
return exchange -> {
// 1. 从请求头获取用户ID(已通过认证的请求,由AuthFilter注入)
String userId = exchange.getRequest().getHeaders().getFirst("X-User-Id");
if (StringUtils.hasText(userId)) {
return Mono.just(userId);
}
// 2. 未认证请求(如商品详情查询),从请求参数获取设备ID(避免匿名用户限流冲突)
String deviceId = exchange.getRequest().getQueryParams().getFirst("deviceId");
if (StringUtils.hasText(deviceId)) {
return Mono.just(deviceId);
}
// 3. 无设备ID,返回默认键(所有匿名请求共享一个限流额度)
return Mono.just("default_anonymous");
};
}
/**
* 接口维度限流键解析器(可选,按接口路径限流)
*/
@Bean("apiKeyResolver")
public KeyResolver apiKeyResolver() {
return exchange -> {
String path = exchange.getRequest().getURI().getPath();
return Mono.just(path);
};
}
}
3.2 熔断降级配置(Resilience4j)
整合Resilience4j实现服务熔断,当后端服务(如订单服务)连续报错超过阈值时,触发熔断并返回默认降级响应,代码如下:
yaml
# 在application.yml中添加熔断配置
spring:
cloud:
gateway:
routes:
- id: order-service-route
# 其他配置省略...
filters:
- name: CircuitBreaker # 熔断过滤器
args:
name: orderServiceCircuitBreaker # 熔断实例名
fallbackUri: forward:/fallback/order # 降级回调接口
# Resilience4j熔断配置
resilience4j:
circuitbreaker:
instances:
orderServiceCircuitBreaker:
slidingWindowSize: 10 # 滑动窗口大小(10个请求)
failureRateThreshold: 50 # 失败率阈值(50%)
waitDurationInOpenState: 60000 # 熔断打开状态持续时间(60秒)
permittedNumberOfCallsInHalfOpenState: 3 # 半开状态允许请求数(3个)
3.3 降级回调接口实现
定义熔断后的降级响应接口,返回友好提示,代码如下:
java
package cn.juwatech.taoke.gateway.controller;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RestController;
import reactor.core.publisher.Mono;
import java.util.HashMap;
import java.util.Map;
/**
* 熔断降级回调接口
*/
@RestController
public class FallbackController {
/**
* 订单服务降级响应
*/
@GetMapping("/fallback/order")
public Mono<Map<String, Object>> orderFallback() {
Map<String, Object> result = new HashMap<>();
result.put("code", 503);
result.put("message", "订单服务暂时繁忙,请稍后重试");
result.put("data", null);
return Mono.just(result);
}
/**
* 商品服务降级响应
*/
@GetMapping("/fallback/product")
public Mono<Map<String, Object>> productFallback() {
Map<String, Object> result = new HashMap<>();
result.put("code", 503);
result.put("message", "商品查询服务暂时繁忙,建议稍后刷新");
result.put("data", null);
return Mono.just(result);
}
}
四、网关监控与优化
- 监控指标采集 :通过Spring Boot Actuator暴露网关指标(如
/actuator/prometheus
),结合Prometheus+Grafana监控请求量、限流次数、熔断次数; - 性能优化 :开启网关请求压缩(
spring.cloud.gateway.httpclient.compress: true
),减少网络传输量;调整Netty线程池大小(spring.cloud.gateway.httpclient.threads.eventLoopCount: 16
),提升并发处理能力; - 安全加固 :配置HTTPS(
server.ssl
相关参数),强制前端使用加密协议;通过网关过滤器过滤SQL注入、XSS攻击等恶意请求参数。
本文著作权归聚娃科技省赚客app开发者团队,转载请注明出处!