1.概述简介
官网:https://cloud.spring.io/spring-cloud-static/spring-cloud-gateway/2.2.1.RELEASE/reference/html/
简单点说gateway就是api路由的统一管理,在请求到达服务之前网关可以替我们做很多事情,诸如:过滤,权限,熔断,降级,限流,
SpringCloud Gateway是基于WebFlux框架实现的,而WebFlux框架底层则使用了高性能的Reactor模式通信框架Netty。
(在Spring Boot应用程序中,我们可以使用网关gateway来实现请求路由和负载均衡。然而,在使用网关gateway时,可能会出现无法下载文件的问题 我就碰到这个坑 流异常关闭---multipart/form-data request failed. Stream ended unexpected,还有一个坑就是上传文件 不行,因为springboot集成gateway后协议走的netty协议了,因为内部协议的关系可能存在的问题,就把文件服务独立出来了当时用的七彩牛 也有可能是平台问题,只做了独立服务处处理不走网关了。。。。。。才解决)
直接上图简洁点:
工作原理:
上代码:
1.依赖
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-configuration-processor</artifactId>
<optional>true</optional>
</dependency>
<!-- 注册发现-->
<dependency>
<groupId>com.alibaba.cloud</groupId>
<artifactId>spring-cloud-starter-alibaba-nacos-discovery</artifactId>
</dependency>
<!-- 配置管理-->
<dependency>
<groupId>com.alibaba.cloud</groupId>
<artifactId>spring-cloud-starter-alibaba-nacos-config</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-gateway</artifactId>
</dependency>
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
</dependency>
<dependency>
<groupId>weiyu.saas.system</groupId>
<artifactId>weiyu-saas-system-common</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.data</groupId>
<artifactId>spring-data-redis</artifactId>
</dependency>
<dependency>
<groupId>redis.clients</groupId>
<artifactId>jedis</artifactId>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-webmvc</artifactId>
</dependency>
2.创建一个httpLogBean用来封装响应 自己定义即可
@Data
@NoArgsConstructor
@AllArgsConstructor
public class HttpLogBean {
// URI、Method、QueryString、POST请求的Body、响应信息和来源IP
private String uri;
private String method;
private String params;
private String headers;
private String body;
private String response;
private String requestId;
}
3.添加过滤器 权限过滤--LogAndAuthFilter
@Component
@Slf4j
public class LogAndAuthFilter implements GlobalFilter, Ordered {
private final LogService logService;
private final AuthService authService;
public LogAndAuthFilter(LogService logService, AuthService authService){
this.logService = logService;
this.authService = authService;
}
@Override
public Mono<Void> filter(ServerWebExchange exchange, GatewayFilterChain chain) {
if (exchange.getRequest().getPath().toString().contains("/export")){
log.info("跳出导出文件时候改变流");//流中断关闭解决
return chain.filter(exchange);
}
HttpLogBean httpLogBean = new HttpLogBean();
ServerHttpRequest serverHttpRequest = exchange.getRequest();
ServerHttpResponse serverHttpResponse = exchange.getResponse();
String requestId = UniqueIdUtil.newUniqueId("requestId");
String method = serverHttpRequest.getMethodValue().toUpperCase();
httpLogBean.setMethod(method);
httpLogBean.setUri(serverHttpRequest.getURI().toString());
httpLogBean.setRequestId(requestId);
MultiValueMap<String, String> query = serverHttpRequest.getQueryParams();
Map<String, String> params = new HashMap<>();
query.forEach((k, v) -> {
params.put(k, v.get(0));
});
httpLogBean.setParams(JSON.toJSONString(params));
if("POST".equals(method)) {
DataBuffer body = (DataBuffer) exchange.getAttributes().get("cachedRequestBody");
httpLogBean.setBody(body.toString());
}
//权限校验
if (authService.isIgnoreAuth(serverHttpRequest)){
return rute(exchange,chain,httpLogBean);
}else{
ResponseResult b = authService.checkToken(serverHttpRequest);
if(b.getCode()!= StatusCode.SUCCESS.code) {
String resp = JSON.toJSONString(b);
httpLogBean.setResponse(resp);
logService.logCreate(httpLogBean);
DataBuffer bodyDataBuffer = serverHttpResponse.bufferFactory().wrap(resp.getBytes());
serverHttpResponse.getHeaders().add("Content-Type", "application/json;charset=UTF-8");
return serverHttpResponse.writeWith(Mono.just(bodyDataBuffer));
}
}
String account = authService.getAccount(serverHttpRequest); //获取账号
ServerHttpRequest newServerHttpRequest = serverHttpRequest.mutate().headers(new Consumer<HttpHeaders>() {
@Override
public void accept(HttpHeaders httpHeaders) {
httpHeaders.remove("Authorization");
if (account!=null){
httpHeaders.add("user", account);
}
httpHeaders.add("requestId", requestId);
}
}).build();
ServerWebExchange build = exchange.mutate().request(newServerHttpRequest).build();
return rute(build,chain,httpLogBean);
}
@Override
public int getOrder() {
return -1;//order顺序不能错 否者启动报错这里为-1
}
public Mono<Void> rute(ServerWebExchange exchange, GatewayFilterChain chain,HttpLogBean httpLogBean){
ServerHttpResponse serverHttpResponse = exchange.getResponse();
DataBufferFactory bufferFactory = serverHttpResponse.bufferFactory();
ServerHttpResponseDecorator decoratedResponse = new ServerHttpResponseDecorator(serverHttpResponse) {
@Override
public Mono<Void> writeWith(Publisher<? extends DataBuffer> body) {
if (body instanceof Flux) {
Flux<? extends DataBuffer> fluxBody = (Flux<? extends DataBuffer>) body;
return super.writeWith(fluxBody.map(dataBuffer -> {
byte[] content = new byte[dataBuffer.readableByteCount()];
dataBuffer.read(content);
DataBufferUtils.release(dataBuffer);
String resp = new String(content, Charset.forName("UTF-8"));
System.out.println("===============返回");
httpLogBean.setResponse(resp);
logService.logCreate(httpLogBean);
byte[] uppedContent = new String(content, Charset.forName("UTF-8")).getBytes();
return bufferFactory.wrap(uppedContent);
}));
}else {
System.out.println("1111111111111111");
}
return super.writeWith(body);
}
};
return chain.filter(exchange.mutate().response(decoratedResponse).build());
}
}
Component
public class CachePostBodyFilter implements GlobalFilter, Ordered {
@Override
public Mono<Void> filter(ServerWebExchange exchange, GatewayFilterChain chain) {
ServerHttpRequest serverHttpRequest = exchange.getRequest();
String method = serverHttpRequest.getMethodValue();
if("POST".equalsIgnoreCase(method)) {
ServerRequest serverRequest = new DefaultServerRequest(exchange);
Mono<String> bodyToMono = serverRequest.bodyToMono(String.class);
return bodyToMono.flatMap(body -> {
NettyDataBufferFactory nettyDataBufferFactory = new NettyDataBufferFactory(new UnpooledByteBufAllocator(false));
DataBuffer bodyDataBuffer = nettyDataBufferFactory.wrap(body.getBytes());
//这里要注意 put 进去的buffer 很多地方都是PooledDataBuffer ,但是我每次走到网关就报我的buffer是有问题 这个问题追溯源码好久才定位到 是应该要放DataBuffer类型而不是PooledDataBuffer 坑死我了
exchange.getAttributes().put("cachedRequestBody", bodyDataBuffer);
ServerHttpRequest newRequest = new ServerHttpRequestDecorator(serverHttpRequest) {
@Override
public HttpHeaders getHeaders() {
HttpHeaders httpHeaders = new HttpHeaders();
httpHeaders.putAll(super.getHeaders());
httpHeaders.set(HttpHeaders.TRANSFER_ENCODING, "chunked");
return httpHeaders;
}
@Override
public Flux<DataBuffer> getBody() {
return Flux.just(bodyDataBuffer);
}
};
return chain.filter(exchange.mutate().request(newRequest).build());
});
}
return chain.filter(exchange);
}
@Override
public int getOrder() {
return -2;//这里order是-2
}
4.配置白名单:
package weiyu.saas.system.gateway.config;
import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.context.annotation.Configuration;
import java.util.List;
@Configuration
@ConfigurationProperties(prefix = "system")
public class TokenWhiteListConfig {
// 配置文件使用list接收
private List<String> whiteList;
public List<String> getWhiteList() {
return whiteList;
}
public void setWhiteList(List<String> whiteList) {
this.whiteList = whiteList;
}
}
//bootstrap.yml 配置
# 访问白名单
system:
whiteList:
- /aa/login/loginTest
- /allauth/login/loginTest
5.回调函数
@RestController
public class FallbackController {
@RequestMapping("/fallback")
public ResponseResult defaultFallback() {
return new ResponseResult(StatusCode.SERVER_ERROR.code,"服务异常","");
}
}
最后启动类加@EnableDiscoveryClient注解 大功告成 注意:如果请求体没有参数 只能get请求
,否者网关那边是不会转发过来的