文章目录
1 摘要
Spring Cloud Gateway 网关除了提供路由功能外,也提供了过滤功能。本文将介绍基于 Spring Cloud Alibaba 2.2 集成 Gateway 网关实现全局过滤功能。
关于 Gateway 与 Zuul 区别:
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 Alibaba 集成网关Gateway
6 Github 源码
Gtihub 源码地址 : https://github.com/Flying9001/springBootDemo
个人公众号:404Code,分享半个互联网人的技术与思考,感兴趣的可以关注.