Zuul實(shí)現(xiàn)動(dòng)態(tài)路由與權(quán)限過濾器方式
前言介紹
在實(shí)際的業(yè)務(wù)開發(fā)中不只是將路由配置放到文件中,而是需要進(jìn)行動(dòng)態(tài)管理并且可以在變化時(shí)不用重啟系統(tǒng)就可以更新。與此同時(shí)還需要在接口訪問的時(shí)候,可以增加一些權(quán)限驗(yàn)證以防止惡意訪問。
1.Filter過濾器,通過繼承實(shí)現(xiàn)對(duì)應(yīng)方法可以進(jìn)行控制過濾;
PRE:這種過濾器在請(qǐng)求被路由之前調(diào)用。我們可利用這種過濾器實(shí)現(xiàn)身份驗(yàn)證、在集群中選擇請(qǐng)求的微服務(wù)、記錄調(diào)試信息等。ROUTING:這種過濾器將請(qǐng)求路由到微服務(wù)。這種過濾器用于構(gòu)建發(fā)送給微服務(wù)的請(qǐng)求,并使用 Apache HttpClient 或 Netfilx Ribbon 請(qǐng)求微服務(wù)。POST:這種過濾器在路由到微服務(wù)以后執(zhí)行。這種過濾器可用來為響應(yīng)添加標(biāo)準(zhǔn)的 HTTP Header、收集統(tǒng)計(jì)信息和指標(biāo)、將響應(yīng)從微服務(wù)發(fā)送給客戶端等。ERROR:在其他階段發(fā)生錯(cuò)誤時(shí)執(zhí)行該過濾器。 除了默認(rèn)的過濾器類型,Zuul 還允許我們創(chuàng)建自定義的過濾器類型。例如,我們可以定制一種 STATIC 類型的過濾器,直接在 Zuul 中生成響應(yīng),而不將請(qǐng)求轉(zhuǎn)發(fā)到后端的微服務(wù)。
2.自定義路由,同構(gòu)實(shí)現(xiàn)SimpleRouteLocator和RefreshableRouteLocator自動(dòng)刷新
- protected Map<String, ZuulRoute> locateRoutes():此方法是加載路由配置的,父類中是獲取properties中的路由配置,可以通過擴(kuò)展此方法,達(dá)到動(dòng)態(tài)獲取配置的目的
- public Route getMatchingRoute(String path):此方法是根據(jù)訪問路徑,獲取匹配的路由配置,父類中已經(jīng)匹配到路由,可以通過路由id查找自定義配置的路由規(guī)則,以達(dá)到根據(jù)自定義規(guī)則動(dòng)態(tài)分流的效果
環(huán)境準(zhǔn)備
- 1.jdk 1.8、idea2018、Maven3
- 2.Spring Boot 2.0.6.RELEASE
- 3.Spring Cloud Finchley.SR2
代碼示例
itstack-demo-springcloud-08
├── itstack-demo-springcloud-eureka-client
│ └── src
│ └── main
│ ├── java
│ │ └── org.itstack.demo
│ │ ├── web
│ │ │ └── EurekaClientController.java
│ │ └── EurekaClientApplication.java
│ └── resources
│ └── application.yml
├── itstack-demo-springcloud-eureka-server
│ └── src
│ └── main
│ ├── java
│ │ └── org.itstack.demo
│ │ └── EurekaServerApplication.java
│ └── resources
│ └── application.yml
├── itstack-demo-springcloud-hystrix-feign
│ └── src
│ └── main
│ ├── java
│ │ └── org.itstack.demo
│ │ ├── service
│ │ │ ├── hystrix
│ │ │ │ └── FeignServiceHystrix.java
│ │ │ └── FeignService.java
│ │ ├── web
│ │ │ └── FeignController.java
│ │ └── FeignApplication.java
│ └── resources
│ └── application.yml
├── itstack-demo-springcloud-hystrix-ribbon
│ └── src
│ └── main
│ ├── java
│ │ └── org.itstack.demo
│ │ ├── service
│ │ │ └── RibbonService.java
│ │ ├── web
│ │ │ └── RibbonController.java
│ │ └── RibbonApplication.java
│ └── resources
│ └── application.yml
└── itstack-demo-springcloud-zuul
└── src
└── main
├── java
│ └── org.itstack.demo
│ ├── config
│ │ └── ZuulConfig.java
│ ├── filter
│ │ └── TokenFilter.java
│ ├── router
│ │ └── RouteLocator.java
│ ├── service
│ │ └── RefreshRouteService.java
│ └── ZuulApplication.java
└── resources
└── application.yml
itstack-demo-springcloud-zuul & 動(dòng)態(tài)路由與權(quán)限過濾
- 1.通過RouteLocator實(shí)現(xiàn)自己的動(dòng)態(tài)路由配置,其實(shí)就是把配置文件內(nèi)容轉(zhuǎn)移到這里用代碼類實(shí)現(xiàn),并且可以根據(jù)需要修改為從數(shù)據(jù)庫里獲取。
- 2.TokenFilter提供了權(quán)限驗(yàn)證功能,當(dāng)用戶訪問時(shí)候會(huì)帶上token否則攔截
- 3.此外還提供了自動(dòng)刷新的接口,用于外部調(diào)用刷新配置
- 4.最后我們需要修改application配置,zuul中還需要排除不做路由的接口[刷新權(quán)限接口]
config/ZuulConfig.java & 路由配置類
@Configuration
public class ZuulConfig {
@Autowired
private ZuulProperties zuulProperties;
@Autowired
private ServerProperties server;
@Bean
public RouteLocator routeLocator() {
return new RouteLocator(this.server.getServlet().getPath(), this.zuulProperties);
}
}
filter/TokenFilter.java & 權(quán)限校驗(yàn)類
public class TokenFilter extends ZuulFilter {
/**
* 過濾器的類型,它決定過濾器在請(qǐng)求的哪個(gè)生命周期中執(zhí)行。
* FilterConstants.PRE_TYPE:代表會(huì)在請(qǐng)求被路由之前執(zhí)行。
* PRE、ROUTING、POST、ERROR
*/
public String filterType() {
return FilterConstants.PRE_TYPE;
}
/**
* filter執(zhí)行順序,通過數(shù)字指定。[數(shù)字越大,優(yōu)先級(jí)越低]
*/
public int filterOrder() {
return 0;
}
/**
* 判斷該過濾器是否需要被執(zhí)行。這里我們直接返回了true,因此該過濾器對(duì)所有請(qǐng)求都會(huì)生效。
* 實(shí)際運(yùn)用中我們可以利用該函數(shù)來指定過濾器的有效范圍。
*/
public boolean shouldFilter() {
return true;
}
/*
* 具體執(zhí)行邏輯
*/
public Object run() {
RequestContext ctx = RequestContext.getCurrentContext();
HttpServletRequest request = ctx.getRequest();
String token = request.getParameter("token");
if (token == null || token.isEmpty()) {
ctx.setSendZuulResponse(false);
ctx.setResponseStatusCode(401);
ctx.setResponseBody("refuse! token is empty");
}
return null;
}
}
router/RouteLocator.java & 路由類
public class RouteLocator extends SimpleRouteLocator implements RefreshableRouteLocator {
private ZuulProperties properties;
public RouteLocator(String servletPath, ZuulProperties properties) {
super(servletPath, properties);
this.properties = properties;
}
@Override
public void refresh() {
doRefresh();
}
@Override
protected Map<String, ZuulRoute> locateRoutes() {
LinkedHashMap<String, ZuulRoute> routesMap = new LinkedHashMap<String, ZuulRoute>();
//從application.properties中加載路由信息
routesMap.putAll(super.locateRoutes());
//從db中加載路由信息
routesMap.putAll(routesConfigGroup());
//優(yōu)化一下配置
LinkedHashMap<String, ZuulRoute> values = new LinkedHashMap<>();
for (Map.Entry<String, ZuulRoute> entry : routesMap.entrySet()) {
String path = entry.getKey();
// Prepend with slash if not already present.
if (!path.startsWith("/")) {
path = "/" + path;
}
if (StringUtils.hasText(this.properties.getPrefix())) {
path = this.properties.getPrefix() + path;
if (!path.startsWith("/")) {
path = "/" + path;
}
}
values.put(path, entry.getValue());
}
return values;
}
/**
* 路由配置組,可以從數(shù)據(jù)庫中讀取
* 基本配置與寫在文件中配置類似,如下;
* # routes:
* # api-a:
* # path: /route-a/**
* # serviceId: itstack-demo-springcloud-feign
* # api-b:
* # path: /route-b/**
* # serviceId: itstack-demo-springcloud-ribbon
* @return 配置組內(nèi)容
*/
private Map<String, ZuulRoute> routesConfigGroup() {
Map<String, ZuulRoute> routes = new LinkedHashMap<>();
ZuulRoute zuulRoute = new ZuulRoute();
zuulRoute.setId("route-a");
zuulRoute.setPath("/route-a/**");
zuulRoute.setServiceId("itstack-demo-springcloud-feign");
// 如果使用了注冊(cè)中心,那么可以根據(jù)serviceId進(jìn)行訪問。
// zuulRoute.setUrl("http://localhost:9001");
zuulRoute.setRetryable(false);
zuulRoute.setStripPrefix(true);
zuulRoute.setSensitiveHeaders(new HashSet<>());
routes.put(zuulRoute.getPath(), zuulRoute);
return routes;
}
}
service/RefreshRouteService.java & 路由刷新服務(wù)
@Service
public class RefreshRouteService {
@Autowired
private ApplicationEventPublisher publisher;
@Autowired
private RouteLocator routeLocator;
public void refreshRoute() {
RoutesRefreshedEvent routesRefreshedEvent = new RoutesRefreshedEvent(routeLocator);
publisher.publishEvent(routesRefreshedEvent);
}
}
ZuulApplication.java & 啟動(dòng)服務(wù)注意注解,另外提供了服務(wù)接口
@SpringBootApplication
@EnableZuulProxy
@EnableEurekaClient
@EnableDiscoveryClient
@RestController
public class ZuulApplication {
public static void main(String[] args) {
SpringApplication.run(ZuulApplication.class, args);
}
@Bean
public TokenFilter tokenFilter() {
return new TokenFilter();
}
@Autowired
private RefreshRouteService refreshRouteService;
@Autowired
private ZuulHandlerMapping zuulHandlerMapping;
@RequestMapping("api/refresh")
public String refresh(){
refreshRouteService.refreshRoute();
return "success";
}
@RequestMapping("api/queryRouteInfo")
@ResponseBody
public Map<String, Object> queryRouteInfo(){
return zuulHandlerMapping.getHandlerMap();
}
}
application.yml & 配置文件修改,路由過濾
server:
port: 10001
spring:
application:
name: itstack-demo-ddd-zuul
eureka:
client:
serviceUrl:
defaultZone: http://localhost:7397/eureka/
# 動(dòng)態(tài)路由,以下配置注釋;
# http://localhost:10001/route-a/api/queryUserInfo?userId=111
# http://localhost:10001/route-b/api/queryUserInfo?userId=111
zuul:
ignoredPatterns: /api/**
# routes:
# api-a:
# path: /route-a/**
# serviceId: itstack-demo-springcloud-feign
# api-b:
# path: /route-b/**
# serviceId: itstack-demo-springcloud-ribbon
測(cè)試驗(yàn)證
1.分別啟動(dòng)如下服務(wù);
- itstack-demo-springcloud-eureka-server 服務(wù)注冊(cè)與發(fā)現(xiàn)
- itstack-demo-springcloud-eureka-client 接口提供方
- itstack-demo-springcloud-hystrix-feign 調(diào)用端
- itstack-demo-springcloud-hystrix-ribbon 調(diào)用端
- itstack-demo-springcloud-zuul 路由服務(wù)
2.可測(cè)試接口列表;
- 路由服務(wù):http://localhost:10001/route-a/api/queryUserInfo?userId=111&token=111
Hello | 111 >: from eureka client port: 8001 From Feign
- 刷新配置:http://localhost:10001/api/refresh
- 內(nèi)容配置:http://localhost:10001/api/queryRouteInfo
綜上總結(jié)
路由服務(wù)可以方便的幫我們控制業(yè)務(wù)類型的區(qū)分訪問,同時(shí)自動(dòng)刷新可以更加方便的使用網(wǎng)關(guān)路由
權(quán)限驗(yàn)證是幾乎不可少的在實(shí)際開發(fā)過程中會(huì)經(jīng)常用到,所有的接口必須是安全可靠的,保證數(shù)據(jù)不泄露
另外還可以考慮從入?yún)⒌挠脩羯矸葸M(jìn)行路由,這樣可以把數(shù)據(jù)庫路由提前,讓不同用戶組直接訪問到不同的數(shù)據(jù)庫組
以上為個(gè)人經(jīng)驗(yàn),希望能給大家一個(gè)參考,也希望大家多多支持腳本之家。
相關(guān)文章
IntelliJ IDEA2019 安裝lombok的實(shí)現(xiàn)
這篇文章主要介紹了IntelliJ IDEA2019 安裝lombok的實(shí)現(xiàn),文中通過示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來一起學(xué)習(xí)學(xué)習(xí)吧2019-10-10
springboot接口如何多次獲取request中的body內(nèi)容
這篇文章主要介紹了springboot接口多次獲取request中的body內(nèi)容的過程,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。如有錯(cuò)誤或未考慮完全的地方,望不吝賜教2021-06-06
springboot項(xiàng)目配置logback-spring.xml實(shí)現(xiàn)按日期歸檔日志的方法
本文主要介紹了springboot項(xiàng)目配置logback-spring.xml實(shí)現(xiàn)按日期歸檔日志的方法,文中通過示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來一起學(xué)習(xí)學(xué)習(xí)吧2024-08-08
SpringBoot3利用AOP實(shí)現(xiàn)IP黑名單功能
在Web應(yīng)用開發(fā)中,控制對(duì)特定IP地址的訪問權(quán)限是一個(gè)常見的需求,通過實(shí)現(xiàn)IP黑白名單功能,我們可以允許某些IP地址訪問應(yīng)用,同時(shí)拒絕其他IP地址的訪問,本文將詳細(xì)介紹SpringBoot3利用AOP實(shí)現(xiàn)IP黑名單功能,并附上相應(yīng)的代碼片段,需要的朋友可以參考下2024-09-09
SpringMVC如何域?qū)ο蠊蚕頂?shù)據(jù)
在Spring MVC中,可以使用域?qū)ο髞砉蚕頂?shù)據(jù),域?qū)ο笫且粋€(gè)Map類型的對(duì)象,可以在請(qǐng)求處理方法之間共享數(shù)據(jù),本文給大家介紹SpringMVC 域?qū)ο蠊蚕頂?shù)據(jù)的示例代碼,一起看看吧2023-09-09
詳解Java編程中線程同步以及定時(shí)啟動(dòng)線程的方法
這篇文章主要介紹了詳解Java編程中線程同步以及定時(shí)啟動(dòng)線程的方法, 講到了wait()與notify()方法以及阻塞隊(duì)列等知識(shí),需要的朋友可以參考下2016-01-01

