从0到1学SpringCloud——15 gateway Filter过滤器的使用

目录

一、前言

二、自定义过滤器

1、权限校验过滤器

2、 路径转发过滤器

3、常用过滤器


一、前言

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;


    }
}

  • 2
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

月夜烛峰

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值