依赖:
<dependencies>
<dependency>
<groupId>com.alibaba.csp</groupId>
<artifactId>sentinel-spring-cloud-gateway-adapter</artifactId>
</dependency>
<!--gateway网关-->
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-gateway</artifactId>
</dependency>
<dependency>
<groupId>com.alibaba.cloud</groupId>
<artifactId>spring-cloud-starter-alibaba-nacos-discovery</artifactId>
</dependency>
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
</dependency>
</dependencies>
application.yml文件
server:
port: 7000
spring:
application:
name: api-gateway
cloud:
gateway:
routes:
- id: order_route #当前路由标识,要求唯一
uri: lb://shop-orders #从nacos中获取服务
order: 1 #路由优先规则,数字越小级别越高
predicates: #断言 路由转发的时候需要满足条件
- Path=/order-ser/** #请求路径要满足这个条件
- Before=2023-07-29T00:00:00.000+08:00[Asia/Shanghai] #限制请求时间在 2023-07-23之前
- Method=GET #限制请求方式为get
- Age=20,70
filters: #过滤器 请求之前传递过程中我们可以通过过滤器进行路径修改
- StripPrefix=1 #转发之前去掉一层路径
- id: product_route #当前路由标识,要求唯一
uri: lb://shop-product #从nacos中获取服务
order: 1 #路由优先规则,数字越小级别越高
predicates: #断言 路由转发的时候需要满足条件
- Path=/product-ser/** #请求路径要满足这个条件
filters: #过滤器 请求之前传递过程中我们可以通过过滤器进行路径修改
- StripPrefix=1 #转发之前去掉一层路径
- SetStatus=20000 #修改的是返回的状态 # Result code==200 msg data
- Log=true,false # 控制日志是否打开
nacos:
discovery:
server-addr: 127.0.0.1:8848
一.过滤器
1.局部过滤器
自定义 局部过滤器【只作用于一个过滤器】
自己配置一个 Log日志的过滤器第一步 : yml中进行设置日志是否打开
- Log=true,false # 控制日志是否打开第二步 :配置 静态内部类
@Data
@NoArgsConstructor
public static class Config{
private boolean consoleLog;
private boolean cacheLog;
}第三步: 自定义一个过滤器工厂,实现方法
@Override
public GatewayFilter apply(LogGatewayFilterFactory.Config config) {@Override
public List<String> shortcutFieldOrder() {添加一个无参数的构造
里面使用 super(LogGatewayFilterFactory.config.class);第四步 :测试
@Component
public class LogGatewayFilterFactory extends AbstractGatewayFilterFactory<LogGatewayFilterFactory.config> {
public LogGatewayFilterFactory() {
super(LogGatewayFilterFactory.config.class);
}
@Override
public GatewayFilter apply(LogGatewayFilterFactory.config config) {
return new GatewayFilter() {
@Override
public Mono<Void> filter(ServerWebExchange exchange, GatewayFilterChain chain) {
if(config.isConsoleLog()){
System.out.println("ConsoleLog-已经开启了.....");
}
if(config.isCacheLog()){
System.out.println("CacheLog+已经开启了....");
}
return chain.filter(exchange);//这个过滤器执行完毕了,移交给下一个过滤器
}
};
}
@Override
public List<String> shortcutFieldOrder() {
return Arrays.asList("consoleLog","cacheLog");
}
@Data
@NoArgsConstructor
public static class config{
private boolean consoleLog;
private boolean cacheLog;
}
}
2.全局过滤器
全局过滤器
作用于所有的路由,无需配置。通过全局过滤器可以实现 权限的统一校验、安全性功能校验。
SpringCloud提供了一系列的全局过滤器 !【内置的全局过滤器 】
LoadBalancerClientFilterWebsocketRoutingFilter
WebClientHttpRoutingFilterWebClientWriteResponseFilter
RouteToRequestUrlFilter
自定义全局过滤器 : 完成权限校验开发中的鉴权逻辑:
1.当客户端第一次进行请求的时候,服务器对用户进行信息认证(登录)
2.认证通过,将用户的信息进行加密形成token,返回给客户端,作为登录凭证
3.每次请求的过程中,客户端都需要携带认证的token
4.服务器需要对token进行解密,判断是否有效
@Component
public class AuthGlobalFilter implements GlobalFilter, Ordered {
@Override
public Mono<Void> filter(ServerWebExchange exchange, GatewayFilterChain chain) {
String token=exchange.getRequest().getQueryParams().getFirst("token");
if (!StringUtils.equals(token,"admin")){
System.out.println("鉴权失败");
exchange.getResponse().setStatusCode(HttpStatus.UNAUTHORIZED);//未经授权的
return exchange.getResponse().setComplete();//完成了
}
return chain.filter(exchange);
}
/**
* 顺序 数值越小,优先级别越高
* @return
*/
@Override
public int getOrder() {
return 0;
}
}
二.断言
限制年龄场景:
假设我们让年龄在20--70之间的人群是可以进入直播间进行刷礼物 !(min,max)步骤如下:
第一步 需要在配置文件中添加一个关于Age的断言
predicates: #断言 路由转发的时候需要满足的条件
- Path=/product-ser/** #请求路径要满足这个需求
- Before=2023-07-29T00:00:00.000+08:00[Asia/Shanghai] #限制请求时间在 2023-07-23之前
- Method=GET #限制请求方式为get
- Age=20,70 #限制20岁到70岁的人群可以访问 --》就是它第二步:开发自定义断言类
public class AgeRoutePredicateFactory extends AbstractRoutePredicateFactory<AgeRoutePredicateFactory.Config> {第三步:
添加静态内部类
@Data
@NoArgsConstructor
public static class Config{
private int minAge;
private int maxAge;}
第四步:
重写两个方法/**
* 读取配置文件中的参数
* @return
*/
@Override
public List<String> shortcutFieldOrder() {
/**
* 断言的逻辑
* @param config
* @return
*/
@Override
public Predicate<ServerWebExchange> apply(AgeRoutePredicateFactory.Config config) {第五步: 测试
@Component
public class AgeRoutePredicateFactory extends AbstractRoutePredicateFactory<AgeRoutePredicateFactory.Config> {
public AgeRoutePredicateFactory() {
super(AgeRoutePredicateFactory.Config.class);
}
/**
* 读取配置文件中的参数
* @return
*/
@Override
public List<String> shortcutFieldOrder() {
return Arrays.asList("minAge","maxAge");
}
/**
* 断言的逻辑
* @param config
* @return
*/
@Override
public Predicate<ServerWebExchange> apply(AgeRoutePredicateFactory.Config config) {
return new Predicate<ServerWebExchange>() {
@Override
public boolean test(ServerWebExchange serverWebExchange) {
//接收前台传递过来的age
String strAge = serverWebExchange.getRequest().getQueryParams().getFirst("age");
if(!StringUtils.isEmpty(strAge)){
int age = Integer.parseInt(strAge);
if(age<config.getMaxAge() && age>config.getMinAge()){
return true;
}else{
return false;
}
}
return false;
}
};
}
@Data
@NoArgsConstructor
public static class Config{
private int minAge;
private int maxAge;
}
}
三.限流
使用网关进行限流
第一步: 添加依赖
<dependency>
<groupId>com.alibaba.csp</groupId>
<artifactId>sentinel-spring-cloud-gateway-adapter</artifactId>
</dependency>
@Configuration
public class GatewayConfiguration {
private final List<ViewResolver> viewResolvers;
private final ServerCodecConfigurer serverCodecConfigurer;
public GatewayConfiguration(List<ViewResolver> viewResolvers, ServerCodecConfigurer serverCodecConfigurer) {
this.viewResolvers = viewResolvers;
this.serverCodecConfigurer = serverCodecConfigurer;
}
//初始化限流过滤器
@Bean
@Order(Ordered.HIGHEST_PRECEDENCE)
public GlobalFilter sentinelGatewayFilter(){
return new SentinelGatewayFilter();
}
//配置初始化的限流参数
@PostConstruct
public void initGatewayRules(){
Set<GatewayFlowRule> rules=new HashSet<>();
//添加咱们资源名 对应的是路由id //统计时间窗口 单位是秒
rules.add(new GatewayFlowRule("product_route").setCount(1).setIntervalSec(1));
//加入网关
GatewayRuleManager.loadRules(rules);
}
//配置限流异常处理接口
@Bean
@Order(Ordered.HIGHEST_PRECEDENCE)
public SentinelGatewayBlockExceptionHandler sentinelGatewayBlockExceptionHandler(){
return new SentinelGatewayBlockExceptionHandler(viewResolvers,serverCodecConfigurer);
}
//自定义一个处理异常的界面
@PostConstruct
public void initBlockExceptionHandler(){
BlockRequestHandler blockRequestHandler=new BlockRequestHandler() {
@Override
public Mono<ServerResponse> handleRequest(ServerWebExchange serverWebExchange, Throwable throwable) {
Map map=new HashMap<>();
map.put("code",500012);
map.put("message","接口被限流了");
return ServerResponse.status(HttpStatus.OK).contentType(MediaType.APPLICATION_JSON_UTF8).body(BodyInserters.fromObject(map));
}
};
GatewayCallbackManager.setBlockHandler(blockRequestHandler);
}
}