SpringCloudGateway过滤器(全局认证、IP拦截、请求参数过滤、响应参数过滤)

全局过滤器(默认对所有路由生效)

全局过滤器(认证)

/**
 * @描述: TODO 全局认证过滤器,不需要在配置文件中配置,作用于所有的请求
 * @作者: lixing
 * @日期 2021/6/17 9:06
 */
@Component
public class MyAuthorizeGlobalFilter implements GlobalFilter, Ordered {
    /** 读取配置文件中的数据 */
    @Resource
    private CustomProperties customProperties;

    /** redis持久化工具类 */
    @Resource
    private RedissonSingleService<String> redissonSingleServiceString;

    /**
     * 过滤器执行的顺序,值越小,执行顺序越靠前
     */
    @Override
    public int getOrder() {
        return GatewayFilterOrderEnum.MY_AUTHORIZE.getValue();
    }

    /**
     * TODO 全局"前置"过滤器逻辑
     */
    @Override
    public Mono<Void> filter(ServerWebExchange exchange, GatewayFilterChain chain) {
        ServerHttpRequest serverHttpRequest = exchange.getRequest();
        ServerHttpResponse serverHttpResponse = exchange.getResponse();

        // 原始请求地址: 127.0.0.1:8800/springCloudGateway/auth/login/doLogin
        // 转化后的地址: /bffAuth/login/doLogin
        String reqPath = serverHttpRequest.getURI().getPath();

        // 全局白名单地址,例如:注册、登陆、忘记密码发送短信等等
        List<String> whiteUrlList = customProperties.getSys().getWhiteUrlList();

        /*
         * 请求白名单过滤
         */
        //   /login/**      匹配 /login 开头
        //   /trip/api/*x   匹配 /trip/api/x,/trip/api/ax,/trip/api/abx ;但不匹配 /trip/abc/x
        //   /trip/a/a?x    匹配 /trip/a/abx;但不匹配 /trip/a/ax,/trip/a/abcx
        //   /**/api/alie   匹配 /trip/api/alie,/trip/dax/api/alie;但不匹配 /trip/a/api
        //   /**/*.htmlm    匹配所有以.htmlm结尾的路径
        PathMatcher pathMatcher = new AntPathMatcher();
        for (String authIgnoreTemp : whiteUrlList) {
            if (StrUtil.isEffective(authIgnoreTemp) && pathMatcher.match(authIgnoreTemp, reqPath)) {
                LoggerUtil.info(MessageFormat.format("MyAuthorizeGlobalFilter 全局认证过滤器 ---- 地址无需登陆:{0}",reqPath));
                return chain.filter(exchange);
            }
        }
        LoggerUtil.info(MessageFormat.format("MyAuthorizeGlobalFilter 全局认证过滤器 ---- 地址需要登陆:{0}",reqPath));
        LoggerUtil.info("");

        /*
         * 对请求进行拦截并校验TOKEN
         */
        String token = serverHttpRequest.getHeaders().getFirst(SysKeyEnum.TOKEN.getKey());
        // 非空校验
        if (!StrUtil.isEffective(token)) {
            // 拦截,提示未授权错误,401
            //serverHttpResponse.setStatusCode(HttpStatus.UNAUTHORIZED);
            // 拦截结束请求
            //return serverHttpResponse.setComplete();
            return MonoResponseUtil.getVoidMono(serverHttpResponse, new BaseResult<String>().fail("TOKEN缺失"));
        }

        // 校验token,获取用户的登录对象
        try {
            BaseLoginUserInfo baseLoginUserInfo = JwtUtil.verifyJwtForHs256(token);

            // redis中校验登录状态
            String versionKey = MessageFormat.format(SysKeyEnum.LOGIN_USER_VERSION.getKey(), SysKeyConstant.PROJECTNAME, baseLoginUserInfo.getUserAccount());
            if (!redissonSingleServiceString.isExists(versionKey)) {
                // redis中没有当前key,则表示登录超时了,需要重写登录
                return MonoResponseUtil.getVoidMono(serverHttpResponse, new BaseResult<String>().fail(SysStateEnum.RESPONSE_STATUS_FALSE_TOKEN_TIMEOUT.getDescribe()));
            } else {
                // 判断当前的version和redis中的version是否一致,不一致则表示当前已经有人登录,需要重新登录
                String tokenVersion = redissonSingleServiceString.getKey(versionKey);
                if (!baseLoginUserInfo.getVersion().equals(tokenVersion)) {
                    return MonoResponseUtil.getVoidMono(serverHttpResponse, new BaseResult<String>().fail(SysStateEnum.RESPONSE_STATUS_FALSE_TOKEN_EXISTS.getDescribe()));
                }
            }

            // 修改请求求
            Consumer<HttpHeaders> httpHeaders = httpHeader -> {
                httpHeader.set(SysKeyEnum.USERINFO_ACCOUNT.getKey(), baseLoginUserInfo.getUserAccount());
                httpHeader.set(SysKeyEnum.USERINFO_PHONE.getKey(), baseLoginUserInfo.getUserAccount());
                httpHeader.remove(SysKeyEnum.TOKEN.getKey());
            };
            // 从原始交换对象获得一个新的 ServerHttpRequest 实例
            ServerHttpRequest serverHttpRequestMutable = serverHttpRequest.mutate().headers(httpHeaders).build();
            // 从原始交换对象获得一个新的 ServerWebExchange 实例
            ServerWebExchange serverWebExchangeMutable = exchange.mutate().request(serverHttpRequestMutable).build();
            //
            return chain.filter(serverWebExchangeMutable).then(Mono.fromRunnable(() -> {
                // TODO 全局"后置"过滤器逻辑
                LoggerUtil.info(MessageFormat.format("MyAuthorizeGlobalFilter 全局认证过滤器 ---- 全局后置过滤器回调逻辑:{0}",reqPath));
            }));
        } catch (Exception e) {
            return MonoResponseUtil.getVoidMono(serverHttpResponse, new BaseResult<String>().fail(e.getMessage()));
        }
    }
}

网关过滤器(局部过滤器,需要为相关路由配置才可生效)

IP网关过滤器

1、创建IP网关过滤器
/**
 * @描述: TODO IP网关过滤器,需要为相关路由配置上才可生效
 * @作者: lixing
 * @日期 2021/6/24 10:25
 */
public class MyIpGatewayFilter implements GatewayFilter, Ordered {
    private CustomProperties customProperties;
    private MyIpGatewayFilterFactoryConfig myIpGatewayFilterFactoryConfig;
    MyIpGatewayFilter(CustomProperties customProperties, MyIpGatewayFilterFactoryConfig myIpGatewayFilterFactoryConfig) {
        this.customProperties = customProperties;
        this.myIpGatewayFilterFactoryConfig = myIpGatewayFilterFactoryConfig;
    }

    /** 过滤器执行的顺序,值越小,执行顺序越靠前 */
    @Override
    public int getOrder() {
        return GatewayFilterOrderEnum.MY_IP.getValue();
    }

    /** 过滤 */
    @Override
    public Mono<Void> filter(ServerWebExchange exchange, GatewayFilterChain chain) {
        if ("*".equalsIgnoreCase(myIpGatewayFilterFactoryConfig.getWhiteIp())) {
            // 对所有ip放行
            return chain.filter(exchange);
        } else {
            ServerHttpRequest serverHttpRequest = exchange.getRequest();
            ServerHttpResponse serverHttpResponse = exchange.getResponse();
            // 请求地址
            String reqPath = serverHttpRequest.getURI().getPath();
            // 当前请求的ip
            String reqIp = Objects.requireNonNull(exchange.getRequest().getRemoteAddress()).getAddress().getHostAddress();
            // 公共白名单列表
            List<String> whiteIpList = customProperties.getSys().getWhiteIpList();
            // 局部白名单列表
            List<String> configIpList = Arrays.asList(myIpGatewayFilterFactoryConfig.getWhiteIp().split(","));
            //
            if (whiteIpList.contains(reqIp) || configIpList.contains(reqIp)) {
                LoggerUtil.info(MessageFormat.format("MyIpGatewayFilter IP网关过滤器 ---- IP无需拦截:{0}", reqIp));
                LoggerUtil.info("");
                return chain.filter(exchange);
            }
            return MonoResponseUtil.getVoidMono(serverHttpResponse, new BaseResult<String>().fail("您还没有权限访问,请联系管理员"));
        }
    }
}
2、创建过滤器参数配置类
/**
 * @描述: TODO IP网关过滤器参数配置类
 * @作者: lixing
 * @日期 2021/6/25 9:22
 */
@Data
@NoArgsConstructor
public class MyIpGatewayFilterFactoryConfig {
    /** 默认白名单ip列表,*:表示对所有ip放行,指定ip列表的格式为:127.0.0.1,192.168.1.110 */
    private String whiteIp;
}
3、创建过滤器工厂
/**
 * @描述: TODO IP网关过滤器工厂,需要为相关路由配置上才可生效
 * @作者: lixing
 * @日期 2021/6/24 10:25
 */
@Component
public class MyIpGatewayFilterFactory extends AbstractGatewayFilterFactory<MyIpGatewayFilterFactoryConfig> {
    @Resource
    private CustomProperties customProperties;

    public MyIpGatewayFilterFactory() {
        super(MyIpGatewayFilterFactoryConfig.class);
    }

    /** 自定义配置类-内部类,用于传入配置参数 */
    @Override
    public List<String> shortcutFieldOrder() {
        return Collections.singletonList("whiteIp");
    }

    /**
     * 过滤器逻辑
     */
    @Override
    public GatewayFilter apply(MyIpGatewayFilterFactoryConfig config) {
        return new MyIpGatewayFilter(customProperties, config);
    }
}
4、配置IP网关过滤器
# TODO 基于 【BFF-AUTH】 服务的路由配置
- id: BFF-AUTH-ROUTE
  uri: lb://bffAuth
  predicates:
    - Path=/springCloudGateway/auth/**
  filters:
    # 自定义 [MyIpGatewayFilter] 网关过滤器。*表示允许所有ip请求,其它格式为:127.0.0.1,192.168.0.110
    - MyIp=127.0.0.1,192.168.1.1

请求参数网关过滤器

1、 创建请求参数网关过滤器
/**
 * @描述: TODO 请求参数网关过滤器,需要为相关路由配置上才可生效
 * @作者: lixing
 * @日期 2021/6/24 10:25
 */
public class MyRequestParamsGatewayFilter implements GatewayFilter, Ordered {

    /** 通过构造函数初始化参数配置类 */
    private MyRequestParamsGatewayFilterFactoryConfig myRequestParamsGatewayFilterFactoryConfig;
    public MyRequestParamsGatewayFilter(MyRequestParamsGatewayFilterFactoryConfig myRequestParamsGatewayFilterFactoryConfig) {
        this.myRequestParamsGatewayFilterFactoryConfig = myRequestParamsGatewayFilterFactoryConfig;
    }

    /** 过滤器执行的顺序,值越小,执行顺序越靠前 */
    @Override
    public int getOrder() {
        return GatewayFilterOrderEnum.MY_REQUEST_PARAMS.getValue();
    }

    /**
     * TODO 网关“前置”过滤器逻辑
     */
    @Override
    public Mono<Void> filter(ServerWebExchange exchange, GatewayFilterChain chain) {
        ServerHttpRequest request = exchange.getRequest();
        //
        String schema = request.getURI().getScheme();
        if ((!"http".equalsIgnoreCase(schema) && !"https".equalsIgnoreCase(schema))) {
            return chain.filter(exchange);
        }
        // 请求地址
        String reqPath = request.getPath().toString();
        // 请求方式:POST
        String method = request.getMethodValue();
        // 媒体类型:浏览器以JSON形式对资源进行解析
        MediaType mediaType = exchange.getRequest().getHeaders().getContentType();
        // 请求中的参数是否需要解密
        exchange.getAttributes().put("isDecrypt", myRequestParamsGatewayFilterFactoryConfig.getIsDecrypt());
        if (SysKeyEnum.GET.getKey().equalsIgnoreCase(method)) {
            /*
             * GET 请求
             */
            MultiValueMap<String, String> queryParams = request.getQueryParams();
            if (SysStateEnum.STATE_YES.getDescribe().equalsIgnoreCase(myRequestParamsGatewayFilterFactoryConfig.getIsDecrypt())) {
                LoggerUtil.info("解密成功");
            }
            return chain.filter(exchange)
                // TODO 网关“后置”过滤器逻辑
                .then(Mono.fromRunnable(() -> {
                    if (SysStateEnum.STATE_YES.getDescribe().equalsIgnoreCase(myRequestParamsGatewayFilterFactoryConfig.getIsPrint())) {
                        LoggerUtil.info("");
                        LoggerUtil.info(MessageFormat.format("MyRequestParamsGatewayFilter 入参全局过滤器 ---- start [GET]:{0}", reqPath));
                        if (SysStateEnum.STATE_YES.getDescribe().equalsIgnoreCase(myRequestParamsGatewayFilterFactoryConfig.getIsDecrypt())) {
                            LoggerUtil.info(MessageFormat.format("入参解密前: {0}", JSONObject.toJSONString(queryParams)));
                            LoggerUtil.info(MessageFormat.format("入参解密后: {0}", JSONObject.toJSONString(queryParams)));
                        } else {
                            LoggerUtil.info(MessageFormat.format("入参: {0}", JSONObject.toJSONString(queryParams)));
                        }
                        LoggerUtil.info("MyRequestParamsGatewayFilter 入参全局过滤器 ---- end");
                    }
            }));
        } else if (SysKeyEnum.POST.getKey().equalsIgnoreCase(method) && MediaType.APPLICATION_JSON.isCompatibleWith(mediaType)) {
            ServerRequest serverRequest = ServerRequest.create(exchange, HandlerStrategies.withDefaults().messageReaders());
            // ServerRequest serverRequest = new DefaultServerRequest(exchange);
            // 读取 body 中的内容并修改
            AtomicReference<JSONObject> bodyJsonObj = new AtomicReference<>(new JSONObject());
            Mono<String> modifiedBody = serverRequest.bodyToMono(String.class).flatMap(body -> {
                bodyJsonObj.set(JSON.parseObject(body));
                if (SysStateEnum.STATE_YES.getDescribe().equalsIgnoreCase(myRequestParamsGatewayFilterFactoryConfig.getIsDecrypt())) {
                    LoggerUtil.info("解密成功");
                }
                return Mono.just(JSONObject.toJSONString(bodyJsonObj));
            });
            /*
             * TODO 下面的将请求体再次封装写回到request里,传到下一级,否则,由于请求体已被消费,后续的服务将取不到值
             * CacheBodyGlobalFilter这个全局过滤器的目的就是把原有的request请求中的body内容读出来,并且使用ServerHttpRequestDecorator这个请求装饰器对request进行包装,重写getBody方法,并把包装后的请求放到过滤器链中传递下去。
             * 这样后面的过滤器中再使用exchange.getRequest().getBody()来获取body时,实际上就是调用的重载后的getBody方法,获取的最先已经缓存了的body数据
             */
            BodyInserter<Mono<String>, ReactiveHttpOutputMessage> bodyInserter = BodyInserters.fromPublisher(modifiedBody, String.class);
            HttpHeaders headers = new HttpHeaders();
            headers.putAll(exchange.getRequest().getHeaders());
            // the new content type will be computed by bodyInserter and then set in the request decorator
            headers.remove(HttpHeaders.CONTENT_LENGTH);
            CachedBodyOutputMessage outputMessage = new CachedBodyOutputMessage(exchange, headers);
            return bodyInserter.insert(outputMessage,  new BodyInserterContext()).then(Mono.defer(() -> {
                ServerHttpRequestDecorator serverHttpRequestDecorator = new ServerHttpRequestDecorator(exchange.getRequest()) {
                    @Override
                    public HttpHeaders getHeaders() {
                        long contentLength = headers.getContentLength();
                        HttpHeaders httpHeaders = new HttpHeaders();
                        httpHeaders.putAll(super.getHeaders());
                        if (contentLength > 0) {
                            httpHeaders.setContentLength(contentLength);
                        } else {
                            httpHeaders.set(HttpHeaders.TRANSFER_ENCODING, "chunked");
                        }
                        return httpHeaders;
                    }
                    @Override
                    public Flux<DataBuffer> getBody() {
                        Flux<DataBuffer> dataBufferFlux = outputMessage.getBody();
                        return dataBufferFlux;
                    }
                };
                // 从原始交换对象获得一个新的 ServerWebExchange 实例
                ServerWebExchange serverWebExchangeMutable = exchange.mutate().request(serverHttpRequestDecorator).build();
                return chain.filter(serverWebExchangeMutable)
                    // TODO 网关“后置”过滤器逻辑
                    .then(Mono.fromRunnable(() -> {
                        if (SysStateEnum.STATE_YES.getDescribe().equalsIgnoreCase(myRequestParamsGatewayFilterFactoryConfig.getIsPrint())) {
                            LoggerUtil.info("");
                            LoggerUtil.info(MessageFormat.format("MyRequestParamsGatewayFilter ---- start [POST]:{0}", reqPath));
                            if (SysStateEnum.STATE_YES.getDescribe().equalsIgnoreCase(myRequestParamsGatewayFilterFactoryConfig.getIsDecrypt())) {
                                LoggerUtil.info(MessageFormat.format("请求入参解密前: {0}", bodyJsonObj.get().toJSONString()));
                                LoggerUtil.info(MessageFormat.format("请求入参解密后: {0}", bodyJsonObj.get().toJSONString()));
                            } else {
                                LoggerUtil.info(MessageFormat.format("请求入参: {0}", bodyJsonObj.get().toJSONString()));
                            }
                            LoggerUtil.info("MyRequestParamsGatewayFilter ---- end");
                            LoggerUtil.info("");
                        }
                }));
            }));
        } else {
            return chain.filter(exchange);
        }
    }
}
2、创建过滤器参数配置类
/**
 * @描述: 请求参数网关过滤器配置类
 * @作者: lixing
 * @日期 2021/6/25 9:22
 */
@Data
@NoArgsConstructor
public class MyRequestParamsGatewayFilterFactoryConfig {
    /** 是否打印请求参数 */
    private String isPrint;
    /** 是否解密请求参数 */
    private String isDecrypt;
}
3、创建过滤器工厂
/**
 * @描述: TODO 请求参数网关过滤器工厂,需要为相关路由配置上才可生效
 * @作者: lixing
 * @日期 2021/6/24 10:25
 */
@Component
public class MyRequestParamsGatewayFilterFactory extends AbstractGatewayFilterFactory<MyRequestParamsGatewayFilterFactoryConfig> {
    /*
     * 构造函数
     */
    public MyRequestParamsGatewayFilterFactory() {
        super(MyRequestParamsGatewayFilterFactoryConfig.class);
    }

    /*
     * 自定义配置类-内部类,用于传入配置参数
     */
    @Override
    public List<String> shortcutFieldOrder() {
        return Arrays.asList("isPrint","isDecrypt");
    }

    /*
     * 过滤器逻辑
     */
    @Override
    public GatewayFilter apply(MyRequestParamsGatewayFilterFactoryConfig config) {
        return new MyRequestParamsGatewayFilter(config);
    }
}
4、配置IP网关过滤器
# TODO 基于 【BFF-AUTH】 服务的路由配置
- id: BFF-AUTH-ROUTE
  uri: lb://bffAuth
  predicates:
    - Path=/springCloudGateway/auth/**
  filters:
    # 自定义 [MyIpGatewayFilter] 网关过滤器。*表示允许所有ip请求,其它格式为:127.0.0.1,192.168.0.110
    - MyIp=127.0.0.1,192.168.1.1
    # 自定义 [MyRequestParamsGatewayFilter] 过滤器。参数1:是否打印请求参数,参数2:是否解密请求参数
    - MyRequestParams=Y,Y
  • 1
    点赞
  • 10
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

打赏作者

大能嘚吧嘚

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

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

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

打赏作者

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

抵扣说明:

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

余额充值