Spring Cloud系列教程(十九):下一代网关服务Gateway-全局Filter(Finchley版本)

官网文档: https://cloud.spring.io/spring-cloud-static/spring-cloud-gateway/2.2.1.RELEASE/reference/html/

一、前言

Spring Cloud Gateway根据过滤器Filter的作用范围划分为GatewayFilterGlobalFilter,二者区别如下:

GatewayFilter : GatewayFilter称为内置过滤器,需要通过 spring.cloud.routes.filters配置在具体路由下,只作用在当前路由上或者特定路由上,可以通过配置 spring.cloud.default-filters,表明作用在所有路由上,GatewayFilter允许以某种方式修改传入的HTTP请求或传出的HTTP响应。路由过滤器适用于特定路由。Spring Cloud Gateway提供了许多内置的GatewayFilter工厂。

GlobalFilter: GlobalFilter称为全局过滤器,不需要在配置文件中配置,作用在所有的路由上,全局有效。

二. GlobalFilter(全局过滤器)

GlobalFilter全局过滤器不需要在配置文件中配置,作用在所有的路由上,最终通过GatewayFilterAdapter包装成GatewayFilterChain识别的过滤器,它为请求业务以及路由的URI转换为真实业务服务的请求地址的核心过滤器,不需要配置,系统初始化时加载,并作用在每个路由上。

Spring Cloud Gateway框架提供的内置的GlobalFilter如下:
在这里插入图片描述

三. GlobalFilter和GatewayFilter区别

GlobalFilter 接口和 GatewayFilter 有一样的接口定义,只不过, GlobalFilter会作用于所有路由,我们可以用它来实现很多统一化处理的业务需求,比如权限认证,IP访问限制等等;而GatewayFilter作用于某一个特定的路由, GlobalFilter全局过滤器是一系列特殊的过滤器,会根据条件应用到所有路由中。GatewayFilter网关过滤器是更细粒度的过滤器,作用于指定的路由中。

四. Gateway提供的GlobalFilter有哪些

gateway自带的GlobalFilter实现类有很多,如下图:
在这里插入图片描述
有转发,路由,负载,WebSocket等相关的GlobalFilter,有兴趣的小伙伴可以下载对应的源码研究下。

五. 如何定义一个GlobalFilter

关于如何定义一个GlobalFilter实现自己的业务逻辑,Gateway官方给出了一个例子:

例子5.1. ExampleConfiguration.java

@Bean
public GlobalFilter customFilter() {
    return new CustomGlobalFilter();
}

/**
 * 自定义全局过滤器
 */
@Component
public class CustomGlobalFilter implements GlobalFilter, Ordered {

    /**
     * 过滤器业务逻辑
     *
     * @param exchange
     * @param chain
     * @return
     */
    @Override
    public Mono<Void> filter(ServerWebExchange exchange, GatewayFilterChain chain) {
        log.info("custom global filter");
        return chain.filter(exchange);
    }
   
    /**
     * 过滤器执行顺序,数值越小,优先级越高
     *
     * @return
     */
    @Override
    public int getOrder() {
        return -1;
    }
}

六. GlobalFilter实现统一鉴权

定义一个AuthFilter过滤器实现 GlobalFilter, Ordered全局过滤器,实现统一鉴权,例如摘自ruoyi-cloud中的实现方案如下:

参考自: https://gitee.com/zhangmrit/ruoyi-cloud/

package com.ruoyi.gateway.fiflt;

import java.io.UnsupportedEncodingException;
import java.util.Arrays;

import javax.annotation.Resource;

import org.apache.commons.lang3.StringUtils;
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.data.redis.core.ValueOperations;
import org.springframework.http.HttpStatus;
import org.springframework.http.server.reactive.ServerHttpRequest;
import org.springframework.http.server.reactive.ServerHttpResponse;
import org.springframework.stereotype.Component;
import org.springframework.web.server.ServerWebExchange;

import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONObject;
import com.ruoyi.common.constant.Constants;
import com.ruoyi.common.core.domain.R;

import lombok.extern.slf4j.Slf4j;
import reactor.core.publisher.Flux;
import reactor.core.publisher.Mono;

/**
 * 网关鉴权
 */
@Slf4j
@Component
public class AuthFilter implements GlobalFilter, Ordered
{
    // 排除过滤的 uri 地址
    // swagger排除自行添加
    private static final String[]           whiteList = {"/auth/login", "/user/register", "/system/v2/api-docs",
            "/auth/captcha/check", "/auth/captcha/get","/auth/login/slide"};

    @Resource(name = "stringRedisTemplate")
    private ValueOperations<String, String> ops;

    @Override
    public Mono<Void> filter(ServerWebExchange exchange, GatewayFilterChain chain)
    {
        String url = exchange.getRequest().getURI().getPath();
        log.info("url:{}", url);
        // 跳过不需要验证的路径
        if (Arrays.asList(whiteList).contains(url))
        {
            return chain.filter(exchange);
        }
        String token = exchange.getRequest().getHeaders().getFirst(Constants.TOKEN);
        // token为空
        if (StringUtils.isBlank(token))
        {
            logger.info( "token is empty..." );
            return setUnauthorizedResponse(exchange, "token can't null or empty string");
        }
        String userStr = ops.get(Constants.ACCESS_TOKEN + token);
        if (StringUtils.isBlank(userStr))
        {
            return setUnauthorizedResponse(exchange, "token verify error");
        }
        JSONObject jo = JSONObject.parseObject(userStr);
        String userId = jo.getString("userId");
        // 查询token信息
        if (StringUtils.isBlank(userId))
        {
            return setUnauthorizedResponse(exchange, "token verify error");
        }
        // 设置userId到request里,后续根据userId,获取用户信息
        ServerHttpRequest mutableReq = exchange.getRequest().mutate().header(Constants.CURRENT_ID, userId)
                .header(Constants.CURRENT_USERNAME, jo.getString("loginName")).build();
        ServerWebExchange mutableExchange = exchange.mutate().request(mutableReq).build();
        return chain.filter(mutableExchange);
    }

    private Mono<Void> setUnauthorizedResponse(ServerWebExchange exchange, String msg)
    {
        ServerHttpResponse originalResponse = exchange.getResponse();
        originalResponse.setStatusCode(HttpStatus.UNAUTHORIZED);
        originalResponse.getHeaders().add("Content-Type", "application/json;charset=UTF-8");
        byte[] response = null;
        try
        {
            response = JSON.toJSONString(R.error(401, msg)).getBytes(Constants.UTF8);
        }
        catch (UnsupportedEncodingException e)
        {
            e.printStackTrace();
        }
        DataBuffer buffer = originalResponse.bufferFactory().wrap(response);
        return originalResponse.writeWith(Flux.just(buffer));
    }

    @Override
    public int getOrder()
    {
        return -200;
    }
}

在上面的AuthFilter需要实现GlobalFilter和Ordered接口,这和实现GatewayFilter很类似。然后根据ServerWebExchange获取ServerHttpRequest,再根据ServerHttpRequest中是否含有参数token,如果没有则完成请求,终止转发,否则执行正常的逻辑。

Thinkingcao CSDN认证博客专家 Java Spring Boot 架构
CSDN2019年度博客之星、博客专家,专注架构、Java、Spring、SpringBoot、SpringCloud、微服务、分布式、中间件、源码分析、JVM性能调优、K8S等领域
微信搜索公众号:「Thinking曹」,一个执着于架构的JAVA基层码农,每天带你学习新知识。
已标记关键词 清除标记
![图片说明](https://img-ask.csdn.net/upload/201910/12/1570868649_468121.png) 环境:maven 3.3.9, jdk1.8, springboot2.1.3, springcloud Greenwich。 模块:注册中心, 配置中心, gateway网关, 用户系统 等。。。。。 需求:使用http/https访问gateway网关,如http://localhost:8080/USER/getuserinfo、https://localhost:8443/USER/getuserinfo 问题描述:我在网上找了一些关于springboot/springcloud配置http/https访问的帖子,基本都类似,例如:springboot2.0.x配置方法,参考https://www.cnblogs.com/lianggp/p/8136540.html 凉瓜皮 ``` @Value("${http.port}") private Integer port; @Bean   public ServletWebServerFactory servletContainer() {     TomcatServletWebServerFactory tomcat = new TomcatServletWebServerFactory();     tomcat.addAdditionalTomcatConnectors(createStandardConnector()); // 添加http     return tomcat;   } // 配置http private Connector createStandardConnector() { Connector connector = new Connector("org.apache.coyote.http11.Http11NioProtocol");![图片说明](https://img-ask.csdn.net/upload/201910/12/1570856177_111958.png) connector.setPort(port); return connector; } ``` 主要都是通过配置tomcat,在创建Connector。问题就出在这个connector上,connector是springboot-web依赖下的,但是在gateway中spring-boot-starter-webflux与spring-boot-web相互冲突,这是因为webfulx默认使用netty,在gateway网关启动的时候就可以看到 ``` : DiscoveryClient_GATEWAY/GA-PC:gateway:8080 - registration status: 204 : Netty started on port(s): 8080 : Updating port to 8080 : Started GatewayApplication in 4.411 seconds (JVM running for 4.82) ``` 所以,这个办法好像行不通,我看帖子里边最终用tomcat启的,不知道他们怎么弄得。 之后,我去官网看了看,各种路由器、过滤器,云里雾里看不太懂。不过发现这个RouteToRequestUrl Filter(将从request里获取的原始url转换成Gateway进行请求转发时所使用的url)过滤器好像可以起点作用,一通搞不顶用。放弃。 我真的不行了,求教各位大佬!!!!
©️2020 CSDN 皮肤主题: 博客之星2020 设计师:CY__0809 返回首页
实付 19.90元
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值