gateway網(wǎng)關(guān)接口請(qǐng)求的校驗(yàn)方式
gateway網(wǎng)關(guān)token的校驗(yàn)
再加入gateway網(wǎng)關(guān)之后,我們?cè)诤笈_(tái)服務(wù)的許多校驗(yàn)操作都可以移動(dòng)到gateway網(wǎng)關(guān), 今天我就來(lái)說(shuō)一下怎么校驗(yàn)請(qǐng)求攜帶的token。
首先我們需要編寫一個(gè)局部過(guò)濾器
繼承AbstractGatewayFilterFactory如下

然后在apply方法中實(shí)現(xiàn)自己要校驗(yàn)的邏輯,所有的請(qǐng)求參數(shù)或者token都可以通過(guò)request獲取。 驗(yàn)證失敗我在catch捕捉異常, 然后將異常信息返回給前端請(qǐng)求,
返回信息的異常處理是這樣的
e.printStackTrace();
ServerHttpResponse response = exchange.getResponse();
JSONObject message = new JSONObject();
message.put("status", -1);
message.put("data", e.getMessage());
byte[] bits = message.toJSONString().getBytes(StandardCharsets.UTF_8);
DataBuffer buffer = response.bufferFactory().wrap(bits);
response.setStatusCode(HttpStatus.UNAUTHORIZED);
//指定編碼,否則在瀏覽器中會(huì)中文亂碼
response.getHeaders().add("Content-Type", "text/plain;charset=UTF-8");
return response.writeWith(Mono.just(buffer));
狀態(tài)碼根據(jù)需求自己定義,異常信息就可以放回給前端請(qǐng)求。
至于校驗(yàn)通過(guò),就直接放行即可
chain.filter(exchange);
如果還需要修改返回的路徑請(qǐng)求可以這樣
// 修改路徑
String newPath ="/test/" + request.getPath();
ServerHttpRequest newRequest = request.mutate()
.path(newPath)
.build();
exchange.getAttributes().put(GATEWAY_REQUEST_URL_ATTR, newRequest.getURI());
//放行
return chain.filter(exchange.mutate()
.request(newRequest).build());
以上就是過(guò)濾器的編寫, 然后就是路由的配置,我是在yml文件中配置
routes:
- id: token_routh
uri: lb://test-service
order: 0
predicates:
- Path=/test/**
filters:
# 去除/test
- StripPrefix=1
- TokenApi
filters下面的TokenApi對(duì)應(yīng)的過(guò)濾器就是剛剛編寫的過(guò)濾器,以上就完成了 對(duì)指定接口校驗(yàn)的功能。
spring cloud gateway網(wǎng)關(guān)請(qǐng)求處理過(guò)程
一、網(wǎng)關(guān)請(qǐng)求處理過(guò)程


客戶端向Spring Cloud Gateway發(fā)出請(qǐng)求。如果網(wǎng)關(guān)處理程序映射確定請(qǐng)求與路由匹配,則將其發(fā)送到網(wǎng)關(guān)Web處理程序。此處理程序運(yùn)行通過(guò)特定于請(qǐng)求的過(guò)濾器鏈發(fā)送請(qǐng)求。濾波器被虛線劃分的原因是濾波器可以在發(fā)送代理請(qǐng)求之前或之后執(zhí)行邏輯。執(zhí)行所有“pre”過(guò)濾器邏輯,然后進(jìn)行代理請(qǐng)求。在發(fā)出代理請(qǐng)求之后,執(zhí)行“post”過(guò)濾器邏輯。
在沒(méi)有端口的路由中定義的URI將分別為HTTP和HTTPS URI獲取默認(rèn)端口設(shè)置為80和443。
DispatcherHandler:所有請(qǐng)求的調(diào)度器,負(fù)載請(qǐng)求分發(fā)RoutePredicateHandlerMapping:路由謂語(yǔ)匹配器,用于路由的查找,以及找到路由后返回對(duì)應(yīng)的WebHandler,DispatcherHandler會(huì)依次遍歷HandlerMapping集合進(jìn)行處理FilteringWebHandler: 使用Filter鏈表處理請(qǐng)求的WebHandler,RoutePredicateHandlerMapping找到路由后返回對(duì)應(yīng)的FilteringWebHandler對(duì)請(qǐng)求進(jìn)行處理,F(xiàn)ilteringWebHandler負(fù)責(zé)組裝Filter鏈表并調(diào)用鏈表處理請(qǐng)求。
1.1、DispatcherHandler分發(fā)處理
在import org.springframework.web.reactive.DispatcherHandler;類中核心方法handle
public Mono<Void> handle(ServerWebExchange exchange) {
if (logger.isDebugEnabled()) {
ServerHttpRequest request = exchange.getRequest();
logger.debug("Processing " + request.getMethodValue() + " request for [" + request.getURI() + "]");
}
//校驗(yàn)handlerMapping集合是否為空
//依次遍歷handlerMapping集合進(jìn)行請(qǐng)求處理
//通過(guò)mapping獲取mapping對(duì)應(yīng)的handler,調(diào)用handler處理
return this.handlerMappings == null ? Mono.error(HANDLER_NOT_FOUND_EXCEPTION) : Flux.fromIterable(this.handlerMappings).concatMap((mapping) -> {
return mapping.getHandler(exchange);
}).next().switchIfEmpty(Mono.error(HANDLER_NOT_FOUND_EXCEPTION)).flatMap((handler) -> {
return this.invokeHandler(exchange, handler);
}).flatMap((result) -> {
return this.handleResult(exchange, result);
});
}
DispatcherHandler的handler執(zhí)行順序
- 校驗(yàn)handlerMapping
- 遍歷Mapping獲取mapping對(duì)應(yīng)的handler(此處會(huì)找到gateway對(duì)應(yīng)的 RoutePredicateHandlerMapping,并通過(guò) RoutePredicateHandlerMapping獲取handler(FilteringWebHandler))
- 通過(guò)handler對(duì)應(yīng)的HandlerAdapter對(duì)handler進(jìn)行調(diào)用(gateway使用的 SimpleHandlerAdapter) 即 FilteringWebHandler與SimpleHandlerAdapter對(duì)應(yīng)

其中SimpleHandlerAdapter中handler實(shí)現(xiàn)
public Mono<HandlerResult> handle(ServerWebExchange exchange, Object handler) {
WebHandler webHandler = (WebHandler)handler;
Mono<Void> mono = webHandler.handle(exchange);
return mono.then(Mono.empty());
}
1.2、RoutePredicateHandlerMapping 路由謂詞處理映射
public class RoutePredicateHandlerMapping extends AbstractHandlerMapping {
private final FilteringWebHandler webHandler;
private final RouteLocator routeLocator;
public RoutePredicateHandlerMapping(FilteringWebHandler webHandler, RouteLocator routeLocator, GlobalCorsProperties globalCorsProperties) {
this.webHandler = webHandler;
this.routeLocator = routeLocator;
//設(shè)置排序字段1,此處的目的是Spring Cloud Gateway 的 GatewayWebfluxEndpoint 提供 HTTP API ,不需要經(jīng)過(guò)網(wǎng)關(guān)
//它通過(guò) RequestMappingHandlerMapping 進(jìn)行請(qǐng)求匹配處理。RequestMappingHandlerMapping 的 order = 0 ,
// 需要排在 RoutePredicateHandlerMapping 前面。所有,RoutePredicateHandlerMapping 設(shè)置 order = 1 。
this.setOrder(1);
this.setCorsConfigurations(globalCorsProperties.getCorsConfigurations());
}
protected Mono<?> getHandlerInternal(ServerWebExchange exchange) {
//設(shè)置mapping到上下文環(huán)境
exchange.getAttributes().put(ServerWebExchangeUtils.GATEWAY_HANDLER_MAPPER_ATTR, this.getClass().getSimpleName());
// 查找路由
return this.lookupRoute(exchange).flatMap((r) -> {
exchange.getAttributes().remove(ServerWebExchangeUtils.GATEWAY_PREDICATE_ROUTE_ATTR);
if (this.logger.isDebugEnabled()) {
this.logger.debug("Mapping [" + this.getExchangeDesc(exchange) + "] to " + r);
}
//將找到的路由信息設(shè)置到上下文環(huán)境中
exchange.getAttributes().put(ServerWebExchangeUtils.GATEWAY_ROUTE_ATTR, r);
//返回mapping對(duì)應(yīng)的WebHandler即FilteringWebHandler
return Mono.just(this.webHandler);
}).switchIfEmpty(Mono.empty().then(Mono.fromRunnable(() -> {
//當(dāng)前未找到路由時(shí)返回空,并移除GATEWAY_PREDICATE_ROUTE_ATTR
exchange.getAttributes().remove(ServerWebExchangeUtils.GATEWAY_PREDICATE_ROUTE_ATTR);
if (this.logger.isTraceEnabled()) {
this.logger.trace("No RouteDefinition found for [" + this.getExchangeDesc(exchange) + "]");
}
})));
}
protected CorsConfiguration getCorsConfiguration(Object handler, ServerWebExchange exchange) {
return super.getCorsConfiguration(handler, exchange);
}
private String getExchangeDesc(ServerWebExchange exchange) {
StringBuilder out = new StringBuilder();
out.append("Exchange: ");
out.append(exchange.getRequest().getMethod());
out.append(" ");
out.append(exchange.getRequest().getURI());
return out.toString();
}
protected Mono<Route> lookupRoute(ServerWebExchange exchange) {
//通過(guò)路由定位器獲取路由信息
return this.routeLocator.getRoutes().concatMap((route) -> {
return Mono.just(route).filterWhen((r) -> {
exchange.getAttributes().put(ServerWebExchangeUtils.GATEWAY_PREDICATE_ROUTE_ATTR, r.getId());
return (Publisher)r.getPredicate().apply(exchange);
}).doOnError((e) -> {
this.logger.error("Error applying predicate for route: " + route.getId(), e);
}).onErrorResume((e) -> {
return Mono.empty();
});
}).next().map((route) -> {
if (this.logger.isDebugEnabled()) {
this.logger.debug("Route matched: " + route.getId());
}
this.validateRoute(route, exchange);
return route;
});
}
protected void validateRoute(Route route, ServerWebExchange exchange) {
}
}
RoutePredicateHandlerMapping的執(zhí)行順序
- 通過(guò)路由定位器獲取全部路由(RouteLocator)
- 通過(guò)路由的謂語(yǔ)(Predicate)過(guò)濾掉不可用的路由信息
- 查找到路由信息后將路由信息設(shè)置當(dāng)上下文環(huán)境中(GATEWAY_ROUTE_ATTR)
- 返回gatway自定的webhandler(FilteringWebHandler)
備注:
在構(gòu)建方法中看到setOrder(1);作用:Spring Cloud Gateway 的 GatewayWebfluxEndpoint 提供 HTTP API ,不需要經(jīng)過(guò)網(wǎng)關(guān)。
通過(guò) RequestMappingHandlerMapping 進(jìn)行請(qǐng)求匹配處理。RequestMappingHandlerMapping 的 order = 0 ,需要排在 RoutePredicateHandlerMapping 前面,所以設(shè)置 order = 1 。
1.3、FilteringWebHandler 過(guò)濾web請(qǐng)求處理
/**
* 通過(guò)過(guò)濾器處理web請(qǐng)求的處理器
*/
public class FilteringWebHandler implements WebHandler {
protected static final Log logger = LogFactory.getLog(FilteringWebHandler.class);
/**
* 全局過(guò)濾器
*/
private final List<GatewayFilter> globalFilters;
public FilteringWebHandler(List<GlobalFilter> globalFilters) {
this.globalFilters = loadFilters(globalFilters);
}
/**
* 包裝加載全局的過(guò)濾器,將全局過(guò)濾器包裝成GatewayFilter
*/
private static List<GatewayFilter> loadFilters(List<GlobalFilter> filters) {
return filters.stream()
.map(filter -> {
//將所有的全局過(guò)濾器包裝成網(wǎng)關(guān)過(guò)濾器
GatewayFilterAdapter gatewayFilter = new GatewayFilterAdapter(filter);
//判斷全局過(guò)濾器是否實(shí)現(xiàn)了可排序接口
if (filter instanceof Ordered) {
int order = ((Ordered) filter).getOrder();
//包裝成可排序的網(wǎng)關(guān)過(guò)濾器
return new OrderedGatewayFilter(gatewayFilter, order);
}
return gatewayFilter;
}).collect(Collectors.toList());
}
@Override
public Mono<Void> handle(ServerWebExchange exchange) {
//獲取請(qǐng)求上下文設(shè)置的路由實(shí)例
Route route = exchange.getRequiredAttribute(GATEWAY_ROUTE_ATTR);
//獲取路由定義下的網(wǎng)關(guān)過(guò)濾器集合
List<GatewayFilter> gatewayFilters = route.getFilters();
//組合全局的過(guò)濾器與路由配置的過(guò)濾器
List<GatewayFilter> combined = new ArrayList<>(this.globalFilters);
//添加路由配置過(guò)濾器到集合尾部
combined.addAll(gatewayFilters);
//對(duì)過(guò)濾器進(jìn)行排序
AnnotationAwareOrderComparator.sort(combined);
logger.debug("Sorted gatewayFilterFactories: "+ combined);
//創(chuàng)建過(guò)濾器鏈表對(duì)其進(jìn)行鏈?zhǔn)秸{(diào)用
return new DefaultGatewayFilterChain(combined).filter(exchange);
}
}
FilteringWebHandler的執(zhí)行順序
- 構(gòu)建一個(gè)包含全局過(guò)濾器的集合(combined)
- 獲取上下中的路由信息GATEWAY_ROUTE_ATTR
- 將路由里的過(guò)濾器添加到集合中(combined)
- 對(duì)過(guò)濾器集合進(jìn)行排序操作
- 通過(guò)過(guò)濾器集合組裝過(guò)濾器鏈表,并進(jìn)行調(diào)用(DefaultGatewayFilterChain與Servlet中的FilterChain與原理是一致的)
- 通過(guò)過(guò)濾器來(lái)處理請(qǐng)求到具體業(yè)務(wù)服務(wù)
以上為個(gè)人經(jīng)驗(yàn),希望能給大家一個(gè)參考,也希望大家多多支持腳本之家。
相關(guān)文章
Java報(bào)錯(cuò):UnsupportedOperationException in Collection
在Java編程中,UnsupportedOperationException是一種常見的運(yùn)行時(shí)異常,通常在試圖對(duì)不支持的操作執(zhí)行修改時(shí)發(fā)生,它表示當(dāng)前操作不被支持,本文將深入探討UnsupportedOperationException的產(chǎn)生原因,并提供具體的解決方案和最佳實(shí)踐,需要的朋友可以參考下2024-06-06
Java線程隊(duì)列LinkedBlockingQueue的使用
本文主要介紹了Java線程隊(duì)列LinkedBlockingQueue的使用,文中通過(guò)示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來(lái)一起學(xué)習(xí)學(xué)習(xí)吧2023-06-06
項(xiàng)目打包成jar后包無(wú)法讀取src/main/resources下文件的解決
本文主要介紹了項(xiàng)目打包成jar后包無(wú)法讀取src/main/resources下文件的解決,文中通過(guò)示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來(lái)一起學(xué)習(xí)學(xué)習(xí)吧2022-04-04
關(guān)于SpringCloud灰度發(fā)布的實(shí)現(xiàn)
這篇文章主要介紹了關(guān)于SpringCloud灰度發(fā)布的實(shí)現(xiàn),灰度發(fā)布又稱金絲雀發(fā)布,是在系統(tǒng)升級(jí)的時(shí)候能夠平滑過(guò)渡的一種發(fā)布方式,灰度發(fā)布可以保證整體系統(tǒng)的穩(wěn)定,在初始灰度的時(shí)候就可以發(fā)現(xiàn)、調(diào)整問(wèn)題,以保證其影響度,需要的朋友可以參考下2023-08-08
SpringMVC?RESTFul實(shí)體類創(chuàng)建及環(huán)境搭建
這篇文章主要為大家介紹了SpringMVC?RESTFul實(shí)體類創(chuàng)建及環(huán)境搭建詳解,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進(jìn)步,早日升職加薪2022-05-05
SpringBoot中實(shí)現(xiàn)數(shù)據(jù)字典的示例代碼
這篇文章主要介紹了SpringBoot中實(shí)現(xiàn)數(shù)據(jù)字典的示例代碼,文中通過(guò)示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來(lái)一起學(xué)習(xí)學(xué)習(xí)吧2020-09-09
JavaWeb連接數(shù)據(jù)庫(kù)MySQL的操作技巧
數(shù)據(jù)庫(kù)是編程中重要的一部分,它囊括了數(shù)據(jù)操作,數(shù)據(jù)持久化等各方面。在每一門編程語(yǔ)言中都占有相當(dāng)大的比例。本次,小編以MySQL為例,使用mvc編程思想,給大家講解下javaweb對(duì)數(shù)據(jù)庫(kù)的操作2017-02-02
Mybatis超級(jí)強(qiáng)大的動(dòng)態(tài)SQL語(yǔ)句大全
MyBatis的動(dòng)態(tài)SQL是基于OGNL表達(dá)式的,它可以幫助我們方便的在SQL語(yǔ)句中實(shí)現(xiàn)某些邏輯,下面這篇文章主要給大家介紹了關(guān)于Mybatis超級(jí)強(qiáng)大的動(dòng)態(tài)SQL語(yǔ)句的相關(guān)資料,需要的朋友可以參考下2022-05-05
springboot使用alibaba的druid數(shù)據(jù)庫(kù)連接池錯(cuò)誤的問(wèn)題及解決
這篇文章主要介紹了springboot使用alibaba的druid數(shù)據(jù)庫(kù)連接池錯(cuò)誤的問(wèn)題及解決方案,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。如有錯(cuò)誤或未考慮完全的地方,望不吝賜教2023-02-02

