SpringCloud - Spring Cloud 之 Gateway网关(十三)_MinggeQingchun的博客-CSDN博客
Web 有三大组件(监听器 过滤器 servlet),Spring Cloud GateWay 最主要的功能就是路由转发,而在定义转发规则时主要涉及了以下三个核心概念
1、Route(路由)
2、Predicate(断言)
3、Filter(过滤)
一、Routes路由配置Gateway有两种配置路由方式
1、Java代码配置类路由参考官网给出demo Spring Cloud Gateway
@SpringBootApplication
public class DemogatewayApplication {
@Bean
public RouteLocator customRouteLocator(RouteLocatorBuilder builder) {
return builder.routes()
.route("path_route", r -> r.path("/get")
.uri("http://httpbin.org"))
.route("host_route", r -> r.host("*.myhost.org")
.uri("http://httpbin.org"))
.route("rewrite_route", r -> r.host("*.rewrite.org")
.filters(f -> f.rewritePath("/foo/(?.*)", "/${segment}"))
.uri("http://httpbin.org"))
.route("hystrix_route", r -> r.host("*.hystrix.org")
.filters(f -> f.hystrix(c -> c.setName("slowcmd")))
.uri("http://httpbin.org"))
.route("hystrix_fallback_route", r -> r.host("*.hystrixfallback.org")
.filters(f -> f.hystrix(c -> c.setName("slowcmd").setFallbackUri("forward:/hystrixfallback")))
.uri("http://httpbin.org"))
.route("limit_route", r -> r
.host("*.limited.org").and().path("/anything
@Configuration
public class GatewayRouteConfig {
@Bean
public RouteLocator customRoiteLocator(RouteLocatorBuilder builder){
//以下url从B站中引用
return builder.routes()
.route("movie-id",r->r.path("/movie").uri("https://www.bilibili.com/movie/?spm_id_from=333.1007.0.0"))
.route("douga-id",r->r.path("/v/douga").uri("https://www.bilibili.com/v/douga/?spm_id_from=333.1007.0.0"))
.build();
}
}
启动springboot 类
输入访问 http://localhost:81/movie
即会跳转到相应 url 进行访问资源
2、application.properties 或 application.yml 配置文件即 SpringCloud - Spring Cloud 之 Gateway网关(十三)_MinggeQingchun的博客-CSDN博客
文中的 三、Gateway应用方式
application.properties
server.port=81
#eureka注册中心首页的Application这一栏
spring.application.name=springcloud-7-service-eureka-gateway
#每间隔5s,向Eureka服务注册中心发送一次心跳,证明服务是否依然“存活”
eureka.instance.lease-renewal-interval-in-seconds=2
#告诉服务端,如果10s之内没有发送心跳,就代表故障,将本服务踢出
eureka.instance.lease-expiration-duration-in-seconds=10
#告诉服务端,服务实例以IP作为链接,不是取机器名
eureka.instance.prefer-ip-address=false
#注册服务实例ID,,服务ID必须唯一 springcloud-7-service-eureka-gateway
eureka.instance.instance-id=${spring.application.name}:${server.port}
#注册中心的链接地址 http://eureka8761:8761/eureka,http://eureka8762:8762/eureka,http://eureka8763:8763/eureka
eureka.client.service-url.defaultZone=http://localhost:8761/eureka
#网关路由配置
#开启网关,默认开启
spring.cloud.gateway.enabled=true
#节点 routes 是一个List 对象,其中 routes 集合中中又包含多个对象,每个对象有三个属性(一个 索引[0]代表一个对象)
#路由 id,没有固定规则,但唯一
spring.cloud.gateway.routes[0].id=login-service-route
#匹配后提供服务的路由地址;uri统一资源定位符 url 统一资源标识符
spring.cloud.gateway.routes[0].uri=http://localhost:9001
#以下是断言条件,必选全部符合条件;断言是给某一个路由来设定的一种匹配规则 默认不能作用在动态路由上
#断言,路径匹配,只要Path匹配上了/doLogin 就往 uri 转发 并且将路径带上 注意:Path 中 P 为大写
#也可以全局匹配,如 /service
@Component
public class GlobalFilterConfig implements GlobalFilter, Ordered {
@Override
public Mono filter(ServerWebExchange exchange, GatewayFilterChain chain) {
//针对请求的过滤,拿到请求的header、url、参数等
// HttpServletRequest 是web里面的
// ServerHttpRequest 是webFlux里面(响应式)
ServerHttpRequest request = exchange.getRequest();
String path = request.getURI().getPath();
System.out.println("path====" + path);
HttpHeaders headers = request.getHeaders();
System.out.println("headers====" + headers);
String methodName = request.getMethod().name();
System.out.println("methodName====" + methodName);
//IPV4、IPV6地址
String hostName = request.getRemoteAddress().getHostName();
System.out.println("hostName====" + hostName);
String ip = request.getHeaders().getHost().getHostString();
System.out.println("ip====" + ip);
// 响应相关的数据
ServerHttpResponse response = exchange.getResponse();
//响应头设置编码
response.getHeaders().set("content-type","application/json;charset=utf-8");
HashMap map = new HashMap<>(4);
map.put("code", HttpStatus.UNAUTHORIZED.value());
map.put("msg","你未授权");
ObjectMapper objectMapper = new ObjectMapper();
//将一个map转成一个字节数组
byte[] bytes = new byte[0];
try {
bytes = objectMapper.writeValueAsBytes(map);
} catch (JsonProcessingException e) {
e.printStackTrace();
}
//通过buffer工厂将字节数组包装成一个数据包
DataBuffer wrap = response.bufferFactory().wrap(bytes);
return response.writeWith(Mono.just(wrap));
// 放行 到下一个过滤器
// return chain.filter(exchange);
}
@Override
public int getOrder() {
return 0;
}
}
2、IP、Token认证拦截
1、token认证拦截
import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.databind.ObjectMapper;
import org.springframework.beans.factory.annotation.Autowired;
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.data.redis.core.StringRedisTemplate;
import org.springframework.http.HttpHeaders;
import org.springframework.http.HttpStatus;
import org.springframework.http.server.reactive.ServerHttpRequest;
import org.springframework.http.server.reactive.ServerHttpResponse;
import org.springframework.stereotype.Component;
import org.springframework.util.CollectionUtils;
import org.springframework.util.StringUtils;
import org.springframework.web.server.ServerWebExchange;
import reactor.core.publisher.Mono;
import java.util.Arrays;
import java.util.HashMap;
import java.util.List;
@Component
public class TokenCheckFilter implements GlobalFilter, Ordered {
public static final List ALLOW_URL = Arrays.asList("/springcloud-7-service-eureka-gateway-login/doLogin", "/myUrl","/doLogin");
@Autowired
private StringRedisTemplate redisTemplate;
@Override
public Mono filter(ServerWebExchange exchange, GatewayFilterChain chain) {
ServerHttpRequest request = exchange.getRequest();
String path = request.getURI().getPath();
if (ALLOW_URL.contains(path)) {
return chain.filter(exchange);
}
// 检查
HttpHeaders headers = request.getHeaders();
List authorization = headers.get("Authorization");
if (!CollectionUtils.isEmpty(authorization)) {
String token = authorization.get(0);
if (StringUtils.hasText(token)) {
// 约定好的有前缀的 bearer token
String realToken = token.replaceFirst("bearer ", "");
if (StringUtils.hasText(realToken) && redisTemplate.hasKey(realToken)) {
return chain.filter(exchange);
}
}
}
// 拦截
ServerHttpResponse response = exchange.getResponse();
response.getHeaders().set("content-type","application/json;charset=utf-8");
HashMap map = new HashMap<>(4);
// 返回401
map.put("code", HttpStatus.UNAUTHORIZED.value());
map.put("msg","未授权");
ObjectMapper objectMapper = new ObjectMapper();
byte[] bytes = new byte[0];
try {
bytes = objectMapper.writeValueAsBytes(map);
} catch (JsonProcessingException e) {
e.printStackTrace();
}
DataBuffer wrap = response.bufferFactory().wrap(bytes);
return response.writeWith(Mono.just(wrap));
}
@Override
public int getOrder() {
return 1;
}
2、IP认证拦截
import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.databind.ObjectMapper;
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.http.server.reactive.ServerHttpRequest;
import org.springframework.http.server.reactive.ServerHttpResponse;
import org.springframework.stereotype.Component;
import org.springframework.web.server.ServerWebExchange;
import reactor.core.publisher.Mono;
import java.util.Arrays;
import java.util.HashMap;
import java.util.List;
@Component
public class IPCheckFilter implements GlobalFilter, Ordered {
public static final List BLACK_LIST = Arrays.asList("127.0.0.1", "144.128.139.137");
@Override
public Mono filter(ServerWebExchange exchange, GatewayFilterChain chain) {
ServerHttpRequest request = exchange.getRequest();
String ip = request.getHeaders().getHost().getHostString();
// 查询数据库 看这个ip是否存在黑名单里面
if (!BLACK_LIST.contains(ip)){
return chain.filter(exchange);
}
//拦截
//注:此处如果拦截IP,就要放开 GlobalFilter 的拦截
ServerHttpResponse response = exchange.getResponse();
response.getHeaders().set("content-type","application/json;charset=utf-8");
HashMap map = new HashMap<>(4);
map.put("code", 438);
map.put("msg","你是黑名单");
ObjectMapper objectMapper = new ObjectMapper();
byte[] bytes = new byte[0];
try {
bytes = objectMapper.writeValueAsBytes(map);
} catch (JsonProcessingException e) {
e.printStackTrace();
}
DataBuffer wrap = response.bufferFactory().wrap(bytes);
return response.writeWith(Mono.just(wrap));
}
@Override
public int getOrder() {
return 0;
}
}
3、限流
限流就是限制一段时间内,用户访问资源的次数,减轻服务器压力,限流大致分为两种:
1、IP 限流(5s 内同一个 ip 访问超过 3 次,则限制不让访问,过一段时间才可继续访问)
2.、请求量限流(只要在一段时间内(窗口期),请求次数达到阀值,就直接拒绝后面来的访问了,
过一段时间才可以继续访问)(粒度可以细化到一个 api(url),一个服务)
限流模型:漏斗算法 ,令牌桶算法,窗口滑动算法 计数器算法
令牌桶算法(1)所有的请求在处理之前都需要拿到一个可用的令牌才会被处理;
(2)根据限流大小,设置按照一定的速率往桶里添加令牌
(3)桶设置最大的放置令牌限制,当桶满时、新添加的令牌就被丢弃或者拒绝;
(4)请求达到后首先要获取令牌桶中的令牌,拿着令牌才可以进行其他的业务逻辑,处理完业务逻辑之后,将令牌直接删除;
(5)令牌桶有最低限额,当桶中的令牌达到最低限额的时候,请求处理完之后将不会删除令
牌,以此保证足够的限流
Spring Cloud Gateway 已经内置了一个 RequestRateLimiterGatewayFilterFactory,可以直接使用。
目前 RequestRateLimiterGatewayFilterFactory 的实现依赖于 Redis,所以我们还要引入 spring-boot-starter-data-redis-reactive 依赖
org.springframework.boot
spring-boot-starter-data-redis-reactive
配置类
import org.springframework.cloud.gateway.filter.ratelimit.KeyResolver;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.Primary;
import reactor.core.publisher.Mono;
@Configuration
public class RequestRateLimiterConfig {
// 针对某一个接口,ip来限流,如/doLogin,每一个ip,10s只能访问3次
@Bean
@Primary // 主候选的
public KeyResolver ipKeyResolver() {
return exchange -> Mono.just(exchange.getRequest().getHeaders().getHost().getHostString());
}
// 针对这个路径来限制 /doLogin
// api 就是 接口 外面一般把gateway api网关 新一代网关
@Bean
public KeyResolver apiKeyResolver() {
return exchange -> Mono.just(exchange.getRequest().getPath().value());
}
}
需要添加一个 @Primary // 主候选的 注解,不然报如下错误
启动快速访问
4、CORS跨域配置@Configuration
public class CorsConfig {
@Bean
public CorsWebFilter corsFilter() {
CorsConfiguration config = new CorsConfiguration();
config.addAllowedMethod("*");
config.addAllowedOrigin("*");
config.addAllowedHeader("*");
UrlBasedCorsConfigurationSource source = new UrlBasedCorsConfigurationSource(new PathPatternParser());
source.registerCorsConfiguration("/**", config);
return new CorsWebFilter(source);
}
}
或者 yml 文件配置
spring:
cloud:
gateway:
globalcors:
corsConfigurations: '[/**]': // 针对哪些路径
allowCredentials: true // 这个是可以携带 cookie
allowedHeaders: '*'
allowedMethods: '*' allowedOrigins: '*'



