1. 背景
灵活配置线上接口黑名单,可以临时调用
2. 网关路由配置application.yml
server:
port: 8868
spring:
cloud:
gateway:
globalcors: # 全局的跨域配置
add-to-simple-url-handler-mapping: true # 解决options请求被拦截问题
# options请求 就是一种询问服务器是否浏览器可以跨域的请求
# 如果每次跨域都有询问服务器是否浏览器可以跨域对性能也是损耗
# 可以配置本次跨域检测的有效期maxAge
# 在maxAge设置的时间范围内,不去询问,统统允许跨域
corsConfigurations:
'[/**]':
allowedOriginPatterns: # 允许哪些网站的跨域请求
- "*"
allowedMethods: # 允许的跨域ajax的请求方式
- "GET"
- "POST"
- "DELETE"
- "PUT"
- "OPTIONS"
allowedHeaders: "*" # 允许在请求中携带的头信息
allowCredentials: true # 允许在请求中携带cookie
maxAge: 360000 # 本次跨域检测的有效期(单位毫秒)
# 有效期内,跨域请求不会一直发option请求去增大服务器压力
routes: # 网关路由配置
- id: route_to_provider #service服务,业务逻辑
uri: lb://cloud-k8s-enterprise-service #通过服务发现找到 Service 服务
predicates:
- Path=/pay/**,/wx/callback/**
- id: route_to_server #web服务 入口
uri: lb://cloud-k8s-enterprise-server #通过服务发现找到 Service 服务
predicates:
- Path=/**
#default-filters:
# - name: BlacklistFilter
#BlacklistFilter 在 default-filters 中使用时可能导致了错误。default-filters 是一个全局过滤器的配置,通常这些过滤器需要是 Spring Cloud Gateway 提供的预定义过滤器,而不是自定义的全局过滤器。你的 BlacklistFilter 是自定义的全局过滤器,不能直接放在 default-filters 中。
logging:
level:
org.springframework.cloud.gateway: DEBUG
2.1. 坑点:无法进行路由转发,配置格式;无法加载自定义全局过滤器
spring.cloud.gateway: 注意坑点!!!!!!!
这里如果单独把nacos配置拎出去,注释了,结果网关路由gateway缺少cloud这层配置,一直无法进行路由转发,网关配置未生效
server:
port: 8868
spring:
# application:
# name: service-gateway
# cloud: 注意坑点!!!!!!! 这里有人单独把nacos配置拎出去,注释了,结果网关路由gateway缺少cloud这层配置,一直无法进行路由转发,网关配置未生效
# nacos:
# discovery:
# server-addr: 127.0.0.1:8848
gateway:
globalcors: # 全局的跨域配置
default-filters:
- name: BlacklistFilter
BlacklistFilter 在 default-filters 中使用时可能导致了错误。default-filters 是一个全局过滤器的配置,通常这些过滤器需要是 Spring Cloud Gateway 提供的预定义过滤器,而不是自定义的全局过滤器。你的 BlacklistFilter 是自定义的全局过滤器,不能直接放在 default-filters 中。
可以配置在某个单独的服务下
#单服务黑名单网关配置demo
spring:
cloud:
gateway:
routes:
- id: dynamic_blacklist_route
uri: http://localhost:8080
predicates:
- Path=/api/v1/**
filters:
- name: BlacklistFilter
server:
port: 8868
spring:
# application:
# name: service-gateway
# cloud: 注意坑点!!!!!!! 这里有人单独把nacos配置拎出去,注释了,结果网关路由gateway缺少cloud这层配置,一直无法进行路由转发,网关配置未生效
# nacos:
# discovery:
# server-addr: 127.0.0.1:8848
gateway:
globalcors: # 全局的跨域配置
add-to-simple-url-handler-mapping: true # 解决options请求被拦截问题
# options请求 就是一种询问服务器是否浏览器可以跨域的请求
# 如果每次跨域都有询问服务器是否浏览器可以跨域对性能也是损耗
# 可以配置本次跨域检测的有效期maxAge
# 在maxAge设置的时间范围内,不去询问,统统允许跨域
corsConfigurations:
'[/**]':
allowedOriginPatterns: # 允许哪些网站的跨域请求
- "*"
allowedMethods: # 允许的跨域ajax的请求方式
- "GET"
- "POST"
- "DELETE"
- "PUT"
- "OPTIONS"
allowedHeaders: "*" # 允许在请求中携带的头信息
allowCredentials: true # 允许在请求中携带cookie
maxAge: 360000 # 本次跨域检测的有效期(单位毫秒)
# 有效期内,跨域请求不会一直发option请求去增大服务器压力
routes: # 网关路由配置
- id: route_to_provider #service服务,业务逻辑
uri: lb://cloud-service #通过服务发现找到 Service 服务
predicates:
- Path=/apiv1/**,/apiv2/**
- id: route_to_server #web服务 入口
uri: lb://cloud-server #通过服务发现找到 Service 服务
predicates:
- Path=/**
logging:
level:
org.springframework.cloud.gateway: DEBUG
2.2. 帮助文档
Spring Colud gateway 网关引入转发无效 (404)_gateway_虔浅-云原生
跟着大佬们的文章,想玩一下gateway api网关。经过一系列ctrl+c和ctrl+v的操作,项目的基本就搭建好了;
1.引入依赖 pom.xml
<!--网关依赖-->
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-gateway</artifactId>
</dependency>
<!--不要引入web!不要引入web!不要引入web!gateway中已经包含-->
2.创建启动类
//如果有注册机(nacos,eureka)什么的可以不用引入
//@EnableDiscoveryClient,只需在配置文件里面配置好就行
@SpringBootApplication
public class GatewayServer {
public static void main(String[] args) {
SpringApplication.run(GatewayServer.class,args);
}
}
3.配置文件
server:
port: 9999 #服务端口
spring:
application:
name: gateway #服务名称
cloud:
############# nacos配置中心 start (没有注册机可以不用配置这一块) #############
nacos:
# nacos配置中心 #nacos的配置文件名称(Data Id)叫 服务名称.yml ,
# 组名(Group)
config:
server-addr: http://nacos服务器
file-extension: yml
namespace: xxx #命名空间(md5)
group: 分组名 #分组
#发现配置
discovery:
server-addr: http://www.lang9725.fun/find/
namespace: xxx #命名空间(md5)
group: batw
############# nacos配置中心 end #############
############## 网关配置 start ##############
gateway:
#开启网关,和很多地方说不一样,很多地方都是这个是默认开启的,
#但设置的话网关功能将无效
enabled: true
routes:
- id: server_finance #id 唯一即可
uri: http://localhost:44444 #用转发路径
predicates:
- Path=/test/test/** # **表示转发地址下的全部都可以通过
############## 网关配置 end ##############
这里要一个被转发服务器地址: http://localhost:44444/test/test/任意地址 并保证这个地址没问题,我们的测试网关地址:http://localhost:9999/test/test/任意地址,保证两个地址的返回效果一致(负载均衡效果到达预期)
前期没有配置spring.cloud.gateway.enabled=true,测试网关一直到报404,看了很多大佬debug,不明所以,最后发现是这边配置没有加,而是配置一个spring.cloud.gateway.discovery.locator.enabled=true,后面加了spring.cloud.gateway.enabled=true就可以了,这个东西应该是和版本,和依赖包一定联系吧。
3. nacos配置bootstrap.yml
spring: #springboot的配置
application:
name: cloud-gateway
cloud:
nacos:
config:
server-addr: 127.0.0.1:8848
namespace: cloud-local
file-extension: yml
shared-configs:
- data-id: blacklist.yml # 在 Nacos 中的配置 Data ID
refresh: true # 启用动态刷新
discovery:
server-addr: 127.0.0.1:8848
namespace: cloud-local
4. 黑名单配置blacklist.yml
blacklist:
paths:
- /coupon/manualGrantPowerCard
- /coupon/manualGrantVoucher
4.1. 加载配置类BlacklistConfig.java
import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.cloud.context.config.annotation.RefreshScope;
import org.springframework.stereotype.Component;
import java.util.List;
/**
* @author shenlifang
* @since 黑名单配置类
* @date 2024/10/21 11:16
*/
@Component
@RefreshScope
@ConfigurationProperties(prefix = "blacklist")
public class BlacklistConfig {
private List<String> paths;
// Getter 和 Setter
public List<String> getPaths() {
return paths;
}
public void setPaths(List<String> paths) {
this.paths = paths;
}
}
5. 启动类,扫描包GatewayApplication.java
import com.ly.cloud.gateway.config.BlacklistConfig;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.boot.context.properties.EnableConfigurationProperties;
import org.springframework.cloud.client.discovery.EnableDiscoveryClient;
@SpringBootApplication
@EnableDiscoveryClient
@EnableConfigurationProperties(BlacklistConfig.class)
public class GatewayApplication {
public static void main(String[] args) {
SpringApplication.run(GatewayApplication.class, args);
}
}
6. 自定义全局过滤器BlacklistFilter.java
package com.cloud.gateway.config;
import com.fasterxml.jackson.core.JsonProcessingException;
import org.springframework.cloud.gateway.filter.GatewayFilterChain;
import org.springframework.cloud.gateway.filter.GlobalFilter;
import org.springframework.core.Ordered;
import org.springframework.core.io.buffer.DataBuffer;
import org.springframework.core.io.buffer.DataBufferFactory;
import org.springframework.http.HttpStatus;
import org.springframework.http.MediaType;
import org.springframework.stereotype.Component;
import org.springframework.web.server.ServerWebExchange;
import reactor.core.publisher.Mono;
import java.nio.charset.StandardCharsets;
import java.util.List;
/**
* @author shenlifang
* @date 2024/10/21 11:12
* @since 接口黑名单过滤器
*/
@Component
public class BlacklistFilter implements GlobalFilter, Ordered {
// 黑名单接口路径列表,可以通过配置文件或者数据库来动态管理
private final BlacklistConfig blacklistConfig;
// 通过构造器注入 BlacklistConfig
public BlacklistFilter(BlacklistConfig blacklistConfig) {
this.blacklistConfig = blacklistConfig;
}
@Override
public Mono<Void> filter(ServerWebExchange exchange, GatewayFilterChain chain) {
String path = exchange.getRequest().getURI().getPath();
// 获取配置中的黑名单路径
List<String> blacklistedPaths = blacklistConfig.getPaths();
// 检查请求路径是否在黑名单中
if (isBlacklistedPath(path, blacklistedPaths)) {
// 如果匹配黑名单,返回 403 Forbidden
exchange.getResponse().setStatusCode(HttpStatus.FORBIDDEN);
// return exchange.getResponse().setComplete();
// 设置响应头,表示内容类型为 JSON
exchange.getResponse().getHeaders().setContentType(MediaType.APPLICATION_JSON);
// 返回描述信息
// String responseBody = "{\"successful\":false,\"code\":403,\"message\":\"接口服务禁用: 该接口服务已加入黑名单,禁止访问!\"}";
String responseBody = "{\"error\": \"Access denied: The requested path is blacklisted.\"}";
// 写入响应体
DataBufferFactory bufferFactory = exchange.getResponse().bufferFactory();
DataBuffer buffer = bufferFactory.wrap(responseBody.getBytes(StandardCharsets.UTF_8));
// 完成响应
return exchange.getResponse().writeWith(Mono.just(buffer));
}
// 如果没有匹配黑名单,继续执行后续过滤器链
return chain.filter(exchange);
}
// 判断路径是否在黑名单中
private boolean isBlacklistedPath(String path, List<String> blacklistedPaths) {
return blacklistedPaths.stream().anyMatch(path::startsWith);
}
@Override
public int getOrder() {
return -1;
}
}
7. 优化改造:优先从redis中获取
import org.springframework.cloud.gateway.filter.GatewayFilterChain;
import org.springframework.cloud.gateway.filter.GlobalFilter;
import org.springframework.core.Ordered;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.http.HttpStatus;
import org.springframework.stereotype.Component;
import org.springframework.web.server.ServerWebExchange;
import reactor.core.publisher.Mono;
import java.util.List;
import java.util.concurrent.TimeUnit;
@Component
public class BlacklistFilter implements GlobalFilter, Ordered {
private final BlacklistConfig blacklistConfig;
private final RedisTemplate<String, List<String>> redisTemplate;
// 通过构造器注入 BlacklistConfig 和 RedisTemplate
public BlacklistFilter(BlacklistConfig blacklistConfig, RedisTemplate<String, List<String>> redisTemplate) {
this.blacklistConfig = blacklistConfig;
this.redisTemplate = redisTemplate;
}
@Override
public Mono<Void> filter(ServerWebExchange exchange, GatewayFilterChain chain) {
String path = exchange.getRequest().getURI().getPath();
// 优先从 Redis 中获取黑名单路径
List<String> blacklistedPaths = redisTemplate.opsForValue().get("blacklist_paths");
// 如果 Redis 中没有黑名单路径,则从配置文件中获取并缓存到 Redis
if (blacklistedPaths == null || blacklistedPaths.isEmpty()) {
blacklistedPaths = blacklistConfig.getPaths();
// 将配置文件中的黑名单缓存到 Redis,有效期设置为 10 分钟
redisTemplate.opsForValue().set("blacklist_paths", blacklistedPaths, 10, TimeUnit.MINUTES);
}
// 检查请求路径是否在黑名单中
if (isBlacklistedPath(path, blacklistedPaths)) {
// 如果匹配黑名单,返回 403 Forbidden
exchange.getResponse().setStatusCode(HttpStatus.FORBIDDEN);
return exchange.getResponse().setComplete();
}
// 如果没有匹配黑名单,继续执行后续过滤器链
return chain.filter(exchange);
}
// 判断路径是否在黑名单中
private boolean isBlacklistedPath(String path, List<String> blacklistedPaths) {
return blacklistedPaths.stream().anyMatch(path::startsWith);
}
@Override
public int getOrder() {
return -1;
}
}
8. 网关启动过程中日志打印网关配置属性
如果没打印,查看yaml文件配置是否有问题
2024-10-21 17:14:02.677 DEBUG 8754 --- [ main] o.s.c.gateway.config.GatewayProperties : Routes supplied from Gateway Properties: [RouteDefinition{id='route_to_provider', predicates=[PredicateDefinition{name='Path', args={_genkey_0=/apiv1/**, _genkey_1=/apiv2/**}}], filters=[], uri=lb://cloud-service, order=0, metadata={}}, RouteDefinition{id='route_to_server', predicates=[PredicateDefinition{name='Path', args={_genkey_0=/**}}], filters=[], uri=lb://cloud-server, order=0, metadata={}}]
日志中的 o.s.c.gateway.config.GatewayProperties
是 Spring Cloud Gateway 在启动过程中输出的日志。具体解释如下:
o.s.c.gateway.config.GatewayProperties
:这是类的名称,表示org.springframework.cloud.gateway.config.GatewayProperties
。这个类负责管理 Spring Cloud Gateway 的配置属性,包括网关的路由、过滤器等信息。Routes supplied from Gateway Properties:
:这条日志表明,Spring Cloud Gateway 已经从应用的配置文件(例如application.yml
或者bootstrap.yml
)中读取并加载了路由配置。
- RouteDefinition:表示每一条路由定义。
-
- id:路由的唯一标识符,例如
route_to_provider
和route_to_server
。 - predicates:路由谓词,用来匹配请求路径。例如,
Path
谓词匹配/apiv1/**
和/apiv2/**
等路径。 - filters:表示应用在路由上的过滤器,在当前配置中为空(没有应用额外的过滤器)。
- uri:目标 URI,
lb://cloud-service
表示通过负载均衡(LoadBalancer)转发请求到服务cloud-service
。 - order:路由的优先级,值越小优先级越高。
- metadata:存储路由的元数据,当前为空。
- id:路由的唯一标识符,例如
Spring Cloud Gateway 从配置文件中正确读取并加载了两个路由定义,分别是 route_to_provider
和 route_to_server
,并根据配置的路径规则(Path
)匹配请求并进行转发。
如果路由匹配不正确,可以通过查看这些日志确认路由是否如预期加载。