Spring Cloud Alibaba 集成网关 Gateway 全局过滤功能


1 摘要

Spring Cloud Gateway 网关除了提供路由功能外,也提供了过滤功能。本文将介绍基于 Spring Cloud Alibaba 2.2 集成 Gateway 网关实现全局过滤功能。

关于 Gateway 与 Zuul 区别:

微服务网关Zuul和Gateway的区别

Spring Cloud Alibaba 2.2 简易集成 Gateway:

Spring Cloud Alibaba 集成网关Gateway

2 核心 Maven 依赖

./cloud-alibaba-gateway-filter/pom.xml
    <dependencies>
        <!-- cloud alibaba -->
        <dependency>
            <groupId>com.alibaba.cloud</groupId>
            <artifactId>spring-cloud-starter-alibaba-nacos-discovery</artifactId>
            <version>${spring-cloud-alibaba.version}</version>
        </dependency>
        <!-- cloud feign -->
        <dependency>
            <groupId>org.springframework.cloud</groupId>
            <artifactId>spring-cloud-starter-openfeign</artifactId>
            <version>${spring-cloud-openfeign.version}</version>
        </dependency>
        <!-- cloud gateway -->
        <dependency>
            <groupId>org.springframework.cloud</groupId>
            <artifactId>spring-cloud-starter-gateway</artifactId>
            <version>${spring-cloud-gateway.version}</version>
        </dependency>

        
        <!-- 省略其他依赖 -->

    </dependencies>

    <dependencyManagement>
        <dependencies>
            <dependency>
                <groupId>com.alibaba.cloud</groupId>
                <artifactId>spring-cloud-alibaba-dependencies</artifactId>
                <version>${spring-cloud-alibaba.version}</version>
                <type>pom</type>
                <scope>import</scope>
            </dependency>
        </dependencies>
    </dependencyManagement>

其中 ${spring-cloud-alibaba.version} 的版本为 2.2.3.RELEASE , ${spring-cloud-gateway.version} 的版本为 2.2.5.RELEASE${spring-cloud-openfeign.version} 的版本为 2.2.5.RELEASE

注意事项: Spring Cloud Alibaba 2.2.3.RELEASE 版本支持的 Spring Boot 版本为 2.3.1.RELEASE ,建议在搭建项目时要保持版本的一致性,Spring Boot 版本过高或过低都可能导致不兼容问题

Spring Cloud Gateway 不能和 Spring Boot Web 一起打包

3 核心代码

3.1 application 配置
./cloud-alibaba-gateway-filter/src/main/resources/application.yml
## config

## server
server:
  port: 8612

## spring
spring:
  application:
    name: cloud-alibaba-gateway-filter
  cloud:
    nacos:
      discovery:
        server-addr: 172.16.140.10:8688
    gateway:
      discovery:
        locator:
          enabled: true


## endpoint
management:
  endpoints:
    web:
      exposure:
        include: "*"

## log
#logging:
#  level:
#    org.springframework.cloud.gateway: debug
3.2 全局拦截器-鉴权
./cloud-alibaba-gateway-filter/src/main/java/com/ljq/demo/springboot/alibaba/gateway/filter/interceptor/AuthFilter.java
package com.ljq.demo.springboot.alibaba.gateway.filter.interceptor;

import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.ljq.demo.springboot.alibaba.gateway.filter.common.api.ApiResult;
import lombok.extern.slf4j.Slf4j;
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.http.HttpHeaders;
import org.springframework.http.HttpStatus;
import org.springframework.http.MediaType;
import org.springframework.http.server.reactive.ServerHttpResponse;
import org.springframework.stereotype.Component;
import org.springframework.util.CollectionUtils;
import org.springframework.web.server.ServerWebExchange;
import reactor.core.publisher.Mono;

import java.util.List;

/**
 * @Description: 鉴权拦截器
 * @Author: junqiang.lu
 * @Date: 2020/12/8
 */
@Slf4j
@Component
public class AuthFilter implements GlobalFilter, Ordered {

    private static final String TOKEN_KEY = "token";

    /**
     * 权限过滤
     *
     * @param exchange
     * @param chain
     * @return
     */
    @Override
    public Mono<Void> filter(ServerWebExchange exchange, GatewayFilterChain chain) {
        List<String> tokenList = exchange.getRequest().getHeaders().get(TOKEN_KEY);
        log.info("token: {}", tokenList);
        if (CollectionUtils.isEmpty(tokenList) || tokenList.get(0).trim().isEmpty()) {
            ServerHttpResponse response = exchange.getResponse();
            // 错误信息
            byte[] data = new byte[0];
            try {
                data = new ObjectMapper().writeValueAsBytes(ApiResult.fail("Token is null"));
            } catch (JsonProcessingException e) {
                e.printStackTrace();
            }
            DataBuffer buffer = response.bufferFactory().wrap(data);
            response.setStatusCode(HttpStatus.UNAUTHORIZED);
            response.getHeaders().add(HttpHeaders.CONTENT_TYPE, MediaType.APPLICATION_JSON_VALUE);
            return response.writeWith(Mono.just(buffer));
        }
        return chain.filter(exchange);
    }

    /**
     * 设置执行级别
     *
     * @return
     */
    @Override
    public int getOrder() {
        return Ordered.LOWEST_PRECEDENCE;
    }
}

简要说明:

当通过该网关的接口,如果请求头没有用户密钥(token),就会直接返回异常,不会继续请求接口

3.3 SpringBoot 启动类
./cloud-alibaba-gateway-filter/src/main/java/com/ljq/demo/springboot/alibaba/gateway/filter/CloudAlibabaGatewayFilterApplication.java
package com.ljq.demo.springboot.alibaba.gateway.filter;

import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.cloud.client.discovery.EnableDiscoveryClient;

/**
 * @author junqiang.lu
 */
@EnableDiscoveryClient
@SpringBootApplication
public class CloudAlibabaGatewayFilterApplication {

    public static void main(String[] args) {
        SpringApplication.run(CloudAlibabaGatewayFilterApplication.class, args);
    }

}
3.4 其他相关代码

返回结果封装类

./cloud-alibaba-gateway-filter/src/main/java/com/ljq/demo/springboot/alibaba/gateway/filter/common/api/ApiResult.java
package com.ljq.demo.springboot.alibaba.gateway.filter.common.api;

import lombok.Data;

import java.util.Map;

/**
 * @Description: 接口返回结果
 * @Author: junqiang.lu
 * @Date: 2020/9/3
 */
@Data
public class ApiResult<T> {

    /**
     * 默认成功结果 key
     */
    public static final String DEFAULT_SUCCESS_KEY = "api.response.success";
    /**
     * 默认成功返回提示信息
     */
    public static final String DEFAULT_SUCCESS_MESSAGE = "SUCCESS";
    /**
     * 默认错误结果 key
     */
    public static final String DEFAULT_ERROR_KEY = "api.response.error";
    /**
     * 默认错误返回提示信息
     */
    public static final String DEFAULT_ERROR_MESSAGE = "ERROR";

    /**
     * 返回结果 key
     */
    private String key;
    /**
     * 返回提示信息
     */
    private String message;
    /**
     * 返回数据
     */
    private T data;
    /**
     * 额外返回数据
     */
    private Map<String, Object> extraDataMap;
    /**
     * 系统当前时间(精确到毫秒)
     */
    private Long timestamp = System.currentTimeMillis();

    protected ApiResult(){
        // 保护无参构造方法,尽量避免手动创建对象
    }

    /**
     * 构造方法
     *
     * @param key
     * @param message
     * @param data
     * @param extraDataMap
     */
    protected ApiResult(String key, String message, T data, Map<String, Object> extraDataMap) {
        this.key = key;
        this.message = message;
        this.data = data;
        this.extraDataMap = extraDataMap;
    }

    /**
     * 成功
     *
     * @return
     */
    public static <T> ApiResult<T> success() {
        return success(null,null);
    }

    /**
     * 成功
     *
     * @param data 返回数据
     * @return
     */
    public static <T> ApiResult<T> success(T data) {
        return success(data, null);
    }

    /**
     * 成功
     *
     * @param data 返回数据
     * @param extraDataMap 额外返回数据
     * @return
     */
    public static <T> ApiResult<T> success(T data, Map<String, Object> extraDataMap) {
        return new ApiResult<>(DEFAULT_SUCCESS_KEY, DEFAULT_SUCCESS_MESSAGE, data, extraDataMap);
    }

    /**
     * 失败
     *
     * @return
     */
    public static <T> ApiResult<T> fail() {
        return fail(DEFAULT_ERROR_KEY, DEFAULT_ERROR_MESSAGE, null, null);
    }

    /**
     * 失败
     *
     * @param message 返回提示信息
     * @return
     */
    public static <T> ApiResult<T> fail(String message) {
        return fail(DEFAULT_ERROR_KEY, message, null, null);
    }

    /**
     * 失败
     *
     * @param responseKey 返回结果 key
     * @param message 返回提示信息
     * @return
     */
    public static <T> ApiResult<T> fail(String responseKey, String message) {
        return fail(responseKey, message, null, null);
    }

    /**
     * 失败
     *
     * @param responseKey 返回结果 Key
     * @param message 返回提示信息
     * @param data 返回数据
     * @param extraDataMap 额外返回数据
     * @return
     */
    public static <T> ApiResult<T> fail(String responseKey, String message, T data, Map<String, Object> extraDataMap) {
        return new ApiResult<>(responseKey, message, data, extraDataMap);
    }


}

至此,一个简单的鉴权网关就实现了。

4 请求测试

启动网关服务(cloud-alibaba-gateway-filter),再启动一个测试服务(cloud-alibaba-server-provider)

测试请求接口

接口地址与请求参数:

http://127.0.0.1:8612/cloud-alibaba-server-provider/api/nacos/hello?name=%E5%BE%B7%E7%8E%9B%E8%A5%BF%E4%BA%9A-Gateway

请求方式:GET

请求头(header):

token = eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJ0b2tlblBob25lIjoiMTg2NjY2NjY2NjZAMTU3NTcwNTUyNzgxMiIsImlzcyI6ImF1dGgwIn0.4Ymy0zrAaWimv1uf_hk1pyZzPORp7BP_PC791s9sU6U

返回结果:

Hello,德玛西亚-Gateway

网关服务后台日志:

2020-12-18 15:37:05.893  INFO 41025 --- [ctor-http-nio-2] c.l.d.s.a.g.f.interceptor.AuthFilter     : token: [eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJ0b2tlblBob25lIjoiMTg2NjY2NjY2NjZAMTU3NTcwNTUyNzgxMiIsImlzcyI6ImF1dGgwIn0.4Ymy0zrAaWimv1uf_hk1pyZzPORp7BP_PC791s9sU6U]

测试服务后台日志:

2020-12-18 15:37:05.903  INFO 41042 --- [nio-8600-exec-3] c.l.d.p.a.s.p.c.NacosProviderController  : serverPort: 8600
2020-12-18 15:37:05.904  INFO 41042 --- [nio-8600-exec-3] c.l.d.p.a.s.p.c.NacosProviderController  : result: Hello,德玛西亚-Gateway

取消 token 的请求头之后

返回结果:

{
    "key": "api.response.error",
    "message": "Token is null",
    "data": null,
    "extraDataMap": null,
    "timestamp": 1608277220597
}

网关服务后台日志:

2020-12-18 15:40:20.597  INFO 41025 --- [ctor-http-nio-2] c.l.d.s.a.g.f.interceptor.AuthFilter     : token: null

测试服务后台无日志

从测试结果可以看出在没有传 token 的情况下,将无法访问到目标服务,从而实现了拦截过滤的功能。

至此,Spring Cloud Alibaba 2.2 集成网关 Gateway 的全局拦截功能已经实现了。

5 推荐参考资料

Spring Cloud Alibaba系列教程 - Spring Cloud Alibaba 网关全局过滤

官方文档: Spring Cloud Gateway

Spring Cloud Alibaba 集成网关Gateway

6 Github 源码

Gtihub 源码地址 : https://github.com/Flying9001/springBootDemo

个人公众号:404Code,分享半个互联网人的技术与思考,感兴趣的可以关注.
404Code

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值