SpringCloud集成GatWay服务网关以及过滤器
摘要:
GateWay与Zuul一样,都是服务网关,所做的事也是一样的,不同在于GateWay是SpringCloud自己的组件,就是为了替代Zuul,SpringCloud高版本已经没有对Zuul2.0进行集成了
一:引入依赖
<dependencies>
<!--引入Eureka的客户端依赖,不需要引入spring-boot-start-web包,因为gateway有自己的启动包-->
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-netflix-eureka-client</artifactId>
</dependency>
<!--引入gateway依赖-->
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-gateway</artifactId>
</dependency>
</dependencies>
二:编写配置文件
server:
port: 10006 #gateway服务端口号
eureka:
client: #Eureka客户端配置,指向注册中心地址
serviceUrl:
defaultZone: http://localhost:10001/eureka/
instance:
prefer-ip-address:true #开启使用IP地址进行注册
instance-id: GatWayServer:10006 #修改实例Id
spring:
application: #指定此服务的应用名称
name: GatWayServer
cloud:
gateway:
discovery:
locator:
enabled: false #指定关闭通过服务名访问,默认就是关闭
lower-case-service-id: true #服务名称小写
routes:
-id: application-user#指定当前路由配置的Id,需要唯一
uri: lb://UserServer#去注册中心找这个服务名
predicates: #断言,匹配访问的路径
-Path=/user/** #服务访问路径
filters:
-StripPrefix=1 #请求转发的时候会去掉/user访问路径,数字代表要去掉的前缀个数
集成完毕!GateWay会根据发送的请求是否以predicates断言的指定路径开头,如果是则指向到uri的微服务
===============================================GateWay过滤器===============================================
摘要:
GateWay的过滤器与Zuul的Filter有相似之处,不同在于GateWay从生命周期上只分为pre和post两种
GateWay的过滤器和自己的断言也有相似之处,都是为了判定请求是否以指定规则一致,是则转发,否则拦截,其实就是一个是配置文件写的,一个是代码实现的,代码实现的可以写更多的逻辑处理
一:自定义单个路由的GateWayFilter【由自定义filter和配置类组成】
创建自定义filter:
package cn.itsource.filter;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.cloud.gateway.filter.GatewayFilter;
import org.springframework.cloud.gateway.filter.GatewayFilterChain;
import org.springframework.core.Ordered;
import org.springframework.web.server.ServerWebExchange;
import reactor.core.publisher.Mono;
/**
* @Description: 自定义一个计算请求调用链耗时的filter
*/
public class RequestTimeFilter implements GatewayFilter, Ordered {
// 创建日志器,可以使用slf4j提供的日志器进行日志打印
private static final Logger log = LoggerFactory.getLogger(RequestTimeFilter.class);
/*
* @Description: 在这个方法中可以写拦截后的逻辑处理,这里是打印了调用链的耗时
* @Author: Director
* @Date: 2022/7/29 18:05
* @param exchange: 服务网络交换器,存放着重要的请求-响应属性、请求实例和响应实例等等,类似于请求上下文对象
* @param chain: Gateway的filter调用链对象
**/
@Override
public Mono<Void> filter(ServerWebExchange exchange, GatewayFilterChain chain) {
// 打印当前客户端请求的地址
String path = exchange.getRequest().getURI().getPath();
log.info("RequestTimeFilter执行,请求路径为:{}", path);
// 存入进入请求时的开始时间
exchange.getAttributes().put("startTime", System.currentTimeMillis());
// 执行完成之后
return chain.filter(exchange).then(
Mono.fromRunnable(() -> {
// 开始时间
Long startTime = exchange.getAttribute("startTime");
// 结束时间
Long endTime = (System.currentTimeMillis() - startTime);
// 打印当前请求耗时
log.info(exchange.getRequest().getURI().getRawPath() + ": " + endTime + "ms");
})
);
}
/*
* @Description: 执行时机,默认为0
**/
@Override
public int getOrder() {
return 0;
}
}
配置filter以及对哪个路由生效:
package cn.itsource.config;
import cn.itsource.filter.RequestTimeFilter;
import org.springframework.cloud.gateway.route.RouteLocator;
import org.springframework.cloud.gateway.route.builder.RouteLocatorBuilder;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
/**
* @Description: 自定义过滤器的配置类
*/
@Configuration // 标识当前类是一个配置类
public class FilterConfig {
// 配置Filter作用于那个访问规则上:/user/**
@Bean
public RouteLocator customerRouteLocator(RouteLocatorBuilder builder) {
return builder.routes().route(r -> r.path("/user/**")
//去掉1个前缀
.filters(f -> f.stripPrefix(1)
.filter(new RequestTimeFilter())
.addResponseHeader("X-Response-test", "test"))
.uri("lb://user-server")
.order(0)
.id("test-RequestTimeFilter")
).build();
}
}
二:自定义针对所有路由的GlobalFiler
与GateWayFilter不同,它不需要配置类进行配置,只需要交给Spring进行管理即可
package cn.itsource.filter;
import com.alibaba.fastjson.JSON;
import lombok.extern.slf4j.Slf4j;
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.HttpStatus;
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.io.UnsupportedEncodingException;
import java.util.HashMap;
import java.util.List;
@Component // 将当前类交给spring管理
@Slf4j // lombok提供的注解使用logger打印器
public class LoginCheckFilter implements GlobalFilter, Ordered {
/*
* @Description: 自定义GlobalFilter的核心方法【判断请求头中是否含有token】
* @param exchange: 服务网络交换器,存放着重要的请求-响应属性、请求实例和响应实例等等,类似于请求上下文对象
* @param chain: filter调用链对象
**/
@Override
public Mono<Void> filter(ServerWebExchange exchange, GatewayFilterChain chain) {
// 1.从请求头中获取token,此处获取到的是一个集合
List<String> token = exchange.getRequest().getHeaders().get("token");
log.info("检查 TOKEN = {}" ,token);
// 2.判断token请求头是否为空,如果为空抛出异常,说明未登录
if(token == null || token.isEmpty()){
// 1.响应对象
ServerHttpResponse response = exchange.getResponse();
// 2.构建错误结果
HashMap<String,Object> data = new HashMap<>();
// 3.设置响应信息
data.put("code", 401);
data.put("success", false);
data.put("message","登录校验失败,请重新登录!");
DataBuffer buffer = null;
try {
// 4.转换参数,包装buffer对象
// 4.1.将数据转换为JSON,再转换为byte数组
byte[] bytes = JSON.toJSONString(data).getBytes("utf-8");
// 4.2.将byte数组包装到DataBuffer对象中,用于响应给浏览器,构建响应内容
buffer = response.bufferFactory().wrap(bytes);
// 5.设置完成响应,不会继续执行后面的filter
// 5.1.设置响应状态为401
response.setStatusCode(HttpStatus.UNAUTHORIZED);
// 5.2.设置响应格式
response.getHeaders().add("Content-Type","application/json;charset=UTF-8");
} catch (UnsupportedEncodingException e) {
e.printStackTrace();
}
// 6.把结果写给客户端
return response.writeWith(Mono.just(buffer));
}
log.info("Token不为空 ,放行");
// 7.token不为空,继续执行后续filter
return chain.filter(exchange);
}
/*
* @Description: filter的执行时机
**/
@Override
public int getOrder() {
return 0;
}
}
三:GateWay跨域配置
spring:
cloud:
globalcors:#跨域配置
cors-configurations:
'[/**]':
allowedOrigins:"https://docs.spring.io"#允许的站点可以为*
allowedMethods:#允许的请求方式
-GET
-POST
-DELETE
-PUT
-HEAD
-CONNECT
-TRACE
-OPTIONS
allowHeaders:#允许的请求头
-Content-Type