目录
一、前言
gateway两个重要的功能,一个是路由,一个是过滤。
gateway中内置了很多过滤器:
根据gateway给出的过滤器规则,我们可有自定义自己的过滤器,用来权限校验或者路径转发。
二、自定义过滤器
1、权限校验过滤器
业务场景:
当为Get请求时,请求被拦截,并返回错误信息;
当为Post请求时,请求通过,不做拦截。
AuthSignatureGatewayFilterFactory.java
package com.zhufeng.gateway.db.filter;
import com.alibaba.fastjson.JSONObject;
import lombok.extern.slf4j.Slf4j;
import org.springframework.cloud.gateway.filter.GatewayFilter;
import org.springframework.cloud.gateway.filter.factory.AbstractGatewayFilterFactory;
import org.springframework.core.io.buffer.DataBuffer;
import org.springframework.core.io.buffer.DataBufferUtils;
import org.springframework.http.server.reactive.ServerHttpRequest;
import org.springframework.http.server.reactive.ServerHttpResponse;
import org.springframework.stereotype.Component;
import org.springframework.util.MultiValueMap;
import reactor.core.publisher.Flux;
import reactor.core.publisher.Mono;
import java.nio.CharBuffer;
import java.nio.charset.StandardCharsets;
import java.util.List;
import java.util.Map;
import java.util.concurrent.atomic.AtomicReference;
/**
* @ClassName: AuthSignatureFilter
* @Description 鉴权
* @author 月夜烛峰
* @date 2022/9/20 19:09
*/
@Slf4j
@Component
public class AuthSignatureGatewayFilterFactory extends AbstractGatewayFilterFactory<AuthSignatureGatewayFilterFactory.Config> {
public AuthSignatureGatewayFilterFactory() {
super(Config.class);
}
@Override
public GatewayFilter apply(AuthSignatureGatewayFilterFactory.Config config) {
return (exchange, chain) -> {
log.info("权限校验...." + config.getSignInfo());
ServerHttpRequest serverHttpRequest = exchange.getRequest();
ServerHttpResponse serverHttpResponse = exchange.getResponse();
String methodName = serverHttpRequest.getMethodValue();
if ("GET".equals(methodName)) {
String resp = "请求被拦截!";
JSONObject signJson = map2json(serverHttpRequest);
log.info("GET——参数:" + signJson);
DataBuffer bodyDataBuffer = serverHttpResponse.bufferFactory().wrap(resp.getBytes());
serverHttpResponse.getHeaders().add("Content-Type", "text/plain;charset=UTF-8");
return serverHttpResponse.writeWith(Mono.just(bodyDataBuffer));
} else if ("POST".equals(methodName)) {
String bodyStr = resolveBodyFromRequest(exchange.getRequest());
log.info("POST——bodyStr:"+bodyStr);
}
return chain.filter(exchange);
};
}
private String resolveBodyFromRequest(ServerHttpRequest serverHttpRequest) {
//获取请求报文体
Flux<DataBuffer> body = serverHttpRequest.getBody();
AtomicReference<String> bodyRef = new AtomicReference<>();
body.subscribe(buffer -> {
CharBuffer charBuffer = StandardCharsets.UTF_8.decode(buffer.asByteBuffer());
DataBufferUtils.release(buffer);
bodyRef.set(charBuffer.toString());
});
log.info("获取请求体:"+bodyRef.get());
//获取request body
return bodyRef.get();
}
public JSONObject map2json(ServerHttpRequest request) {
MultiValueMap<String, String> muMap = request.getQueryParams();
JSONObject reqJson = new JSONObject();
for (Map.Entry<String, List<String>> entry : muMap.entrySet()) {
reqJson.put(entry.getKey(), entry.getValue().get(0));
}
return reqJson;
}
public static class Config {
private JSONObject signInfo;
public JSONObject getSignInfo() {
return signInfo;
}
public Config setSignInfo(JSONObject signInfo) {
this.signInfo = signInfo;
return this;
}
}
}
在路由代码中添加:
//权限校验
route.getFilters().add(new FilterDefinition("AuthSignature"));
关键代码如下:
/**
* 通过数据库配置路由规则
*/
private void load() {
List<JSONObject> routeList = gatewayService.findRouteList();
for(JSONObject routeJson:routeList) {
//配置路由规则
RouteDefinition route= new RouteDefinition();
route.setId(routeJson.getString("routeId"));
URI uri = URI.create(routeJson.getString("uri"));
route.setUri(uri);
//配置请求路径规则
PredicateDefinition path = new PredicateDefinition();
path.setName(routeJson.getString("pathName"));
path.addArg("pattern", routeJson.getString("pathPattern"));
route.getPredicates().add(path);
//配置方法规则:Get、Post等
PredicateDefinition method = new PredicateDefinition();
method.setName(routeJson.getString("methodName"));
String methodPattern = routeJson.getString("methodPattern");
String[] methodArr = methodPattern.split(",");
for(int i=0;i<methodArr.length;i++) {
method.addArg("pattern"+0, methodArr[i]);
}
route.getPredicates().add(method);
//权限校验
route.getFilters().add(new FilterDefinition("AuthSignature"));
routeDefinitions.add(route);
}
}
网关、微服务、数据库配置等完整相关代码见:
《从0到1学SpringCloud——12 gateway 动态配置网关路由规则》
发起Get测试:
响应结果为请求被拦截
发起Post请求测试:
两次请求控制台打印:
说明Get和Post请求都会被过滤器拦截校验。
2、 路径转发过滤器
当访问某一请求路径时,请求被拦截,校验通过后转发到另一个路径
MsgPathGatewayFilterFactory.java代码:
package com.zhufeng.gateway.db.filter;
import lombok.extern.slf4j.Slf4j;
import org.springframework.cloud.gateway.filter.GatewayFilter;
import org.springframework.cloud.gateway.filter.GatewayFilterChain;
import org.springframework.cloud.gateway.filter.factory.AbstractGatewayFilterFactory;
import org.springframework.cloud.gateway.support.ServerWebExchangeUtils;
import org.springframework.http.server.reactive.ServerHttpRequest;
import org.springframework.stereotype.Component;
import org.springframework.web.server.ServerWebExchange;
import reactor.core.publisher.Mono;
/**
* @ClassName: MsgPathGatewayFilterFactor
* @Description 消息路径转发
* @author 月夜烛峰
* @date 2022/9/16 18:45
*/
@Slf4j
@Component
public class MsgPathGatewayFilterFactory extends AbstractGatewayFilterFactory<MsgPathGatewayFilterFactory.Config> {
public MsgPathGatewayFilterFactory() {
super(Config.class);
}
@Override
public GatewayFilter apply(MsgPathGatewayFilterFactory.Config config) {
return new GatewayFilter() {
@Override
public Mono<Void> filter(ServerWebExchange exchange, GatewayFilterChain chain) {
log.info("自定义过滤器...");
ServerHttpRequest req = exchange.getRequest();
ServerHttpRequest request = req.mutate().path(config.getNewPath()).build();
exchange.getAttributes().put(ServerWebExchangeUtils.GATEWAY_REQUEST_URL_ATTR, request.getURI());
return chain.filter(exchange.mutate().request(request).build());
}
};
}
public static class Config {
private String newPath;
public String getNewPath() {
return newPath;
}
public Config setNewPath(String newPath) {
this.newPath = newPath;
return this;
}
}
}
数据库中新增:
INSERT INTO `zf_gateway_route` (`route_id`, `uri`, `path_name`, `path_pattern`, `method_name`, `method_pattern`, `msg_name`, `msg_type`, `filter_name`, `new_path`, `remark`, `status`, `index`) VALUES ('zhufeng-body-user', 'lb://zhufeng-web-msg', 'Path', '/msg/user', 'Method', 'Post', NULL, NULL, 'MsgPath', '/msg/info', '路由测试', 0, 5);
数据库表交过及完整相关代码见:
《从0到1学SpringCloud——12 gateway 动态配置网关路由规则》
业务场景:
当发送Post请求到 /msg/user 时,请求被自动转发至 /msg/info 。
controller代码:
/**
* @ClassName: MsgController
* @Description 消息路由测试
* @author 月夜烛峰
* @date 2022/7/22 20:33
*/
@RequestMapping("msg")
@RestController
public class MsgController {
@RequestMapping("info")
public String showInfo() {
return "good luck~";
}
}
请求测试:
3、常用过滤器
除此之外,经常用到的还有熔断过滤器,限流等过滤器等
熔断过滤器 gateway 已经内置:HystrixGatewayFilterFactory
限流等过滤器核心参考代码:
@Slf4j
@Component
public class RateLimitFilter implements GlobalFilter, Ordered {
@Autowired
private Map<String,RateLimitService> rateLimitServiceMap ;
@Override
public int getOrder() {
return -1;
}
@Override
public Mono<Void> filter(ServerWebExchange exchange, GatewayFilterChain chain) {
log.info("全局过滤器...");
ServerHttpRequest serverHttpRequest = exchange.getRequest();
String method = serverHttpRequest.getMethodValue();
if ("POST".equals(method)) {
//从请求里获取Post请求体
String bodyStr = resolveBodyFromRequest(serverHttpRequest);
log.info("过滤器body:"+bodyStr);
}
String accessToken = TokenUtil.extractToken(exchange.getRequest());
String reqUrl = exchange.getRequest().getPath().value();
AtomicInteger isOpenRateLimit = new AtomicInteger(0);
//超额自增处理
rateLimitServiceMap.forEach((k,v) -> { if(!v.checkRateLimit(reqUrl, accessToken)){ isOpenRateLimit.incrementAndGet();}});
//超额限流
if ( isOpenRateLimit.get() > 0) {
exchange.getResponse().setStatusCode(HttpStatus.TOO_MANY_REQUESTS);
ServerHttpResponse response = exchange.getResponse();
JSONObject message = new JSONObject();
message.put("code", 429);
message.put("msg", "TOO MANY REQUESTS!");
byte[] bits = message.toJSONString().getBytes(StandardCharsets.UTF_8);
DataBuffer buffer = response.bufferFactory().wrap(bits);
response.setStatusCode(HttpStatus.TOO_MANY_REQUESTS);
//指定编码,否则在浏览器中会中文乱码
response.getHeaders().add("Content-Type", "application/json;charset=UTF-8");
return response.writeWith(Mono.just(buffer)).doOnError((error) -> {
DataBufferUtils.release(buffer);
});
}
return chain.filter(exchange);
}
private String resolveBodyFromRequest(ServerHttpRequest serverHttpRequest) {
//获取请求体
Flux<DataBuffer> body = serverHttpRequest.getBody();
AtomicReference<String> bodyRef = new AtomicReference<>();
body.subscribe(buffer -> {
CharBuffer charBuffer = StandardCharsets.UTF_8.decode(buffer.asByteBuffer());
DataBufferUtils.release(buffer);
bodyRef.set(charBuffer.toString());
});
//获取request body
return bodyRef.get();
}
private DataBuffer stringBuffer(String value) {
byte[] bytes = value.getBytes(StandardCharsets.UTF_8);
NettyDataBufferFactory nettyDataBufferFactory = new NettyDataBufferFactory(ByteBufAllocator.DEFAULT);
DataBuffer buffer = nettyDataBufferFactory.allocateBuffer(bytes.length);
buffer.write(bytes);
return buffer;
}
}