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 # 允许在请求中携带cookiemaxAge: 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_routeuri: http://localhost:8080predicates:- 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:8848gateway:globalcors: # 全局的跨域配置add-to-simple-url-handler-mapping: true # 解决options请求被拦截问题# options请求 就是一种询问服务器是否浏览器可以跨域的请求# 如果每次跨域都有询问服务器是否浏览器可以跨域对性能也是损耗# 可以配置本次跨域检测的有效期maxAge# 在maxAge设置的时间范围内,不去询问,统统允许跨域corsConfigurations:'[/**]':allowedOriginPatterns: # 允许哪些网站的跨域请求- "*"allowedMethods: # 允许的跨域ajax的请求方式- "GET"- "POST"- "DELETE"- "PUT"- "OPTIONS"allowedHeaders: "*" # 允许在请求中携带的头信息allowCredentials: true # 允许在请求中携带cookiemaxAge: 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: ymlnamespace: xxx #命名空间(md5)group: 分组名 #分组#发现配置discovery:server-addr: http://www.lang9725.fun/find/namespace: xxx #命名空间(md5)group: batw############# nacos配置中心 end ########################### 网关配置 start ##############gateway:#开启网关,和很多地方说不一样,很多地方都是这个是默认开启的,#但设置的话网关功能将无效enabled: trueroutes:- 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-gatewaycloud:nacos:config:server-addr: 127.0.0.1:8848namespace: cloud-localfile-extension: ymlshared-configs:- data-id: blacklist.yml # 在 Nacos 中的配置 Data IDrefresh: true # 启用动态刷新discovery:server-addr: 127.0.0.1:8848namespace: 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 和 Setterpublic 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;// 通过构造器注入 BlacklistConfigpublic BlacklistFilter(BlacklistConfig blacklistConfig) {this.blacklistConfig = blacklistConfig;}@Overridepublic Mono<Void> filter(ServerWebExchange exchange, GatewayFilterChain chain) {String path = exchange.getRequest().getURI().getPath();// 获取配置中的黑名单路径List<String> blacklistedPaths = blacklistConfig.getPaths();// 检查请求路径是否在黑名单中if (isBlacklistedPath(path, blacklistedPaths)) {// 如果匹配黑名单,返回 403 Forbiddenexchange.getResponse().setStatusCode(HttpStatus.FORBIDDEN);// return exchange.getResponse().setComplete();// 设置响应头,表示内容类型为 JSONexchange.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);}@Overridepublic 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 和 RedisTemplatepublic BlacklistFilter(BlacklistConfig blacklistConfig, RedisTemplate<String, List<String>> redisTemplate) {this.blacklistConfig = blacklistConfig;this.redisTemplate = redisTemplate;}@Overridepublic Mono<Void> filter(ServerWebExchange exchange, GatewayFilterChain chain) {String path = exchange.getRequest().getURI().getPath();// 优先从 Redis 中获取黑名单路径List<String> blacklistedPaths = redisTemplate.opsForValue().get("blacklist_paths");// 如果 Redis 中没有黑名单路径,则从配置文件中获取并缓存到 Redisif (blacklistedPaths == null || blacklistedPaths.isEmpty()) {blacklistedPaths = blacklistConfig.getPaths();// 将配置文件中的黑名单缓存到 Redis,有效期设置为 10 分钟redisTemplate.opsForValue().set("blacklist_paths", blacklistedPaths, 10, TimeUnit.MINUTES);}// 检查请求路径是否在黑名单中if (isBlacklistedPath(path, blacklistedPaths)) {// 如果匹配黑名单,返回 403 Forbiddenexchange.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);}@Overridepublic 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
)匹配请求并进行转发。
如果路由匹配不正确,可以通过查看这些日志确认路由是否如预期加载。