Spring Cloud Alibaba 整合閘道器 Gateway 全域性過濾功能
技術標籤:Javaspring cloud alibabacloud gatewayspring boot閘道器全域性過濾
文章目錄
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,分享半個網際網路人的技術與思考,感興趣的可以關注.