Zuul實現(xiàn)動態(tài)路由與權限過濾器方式
前言介紹
在實際的業(yè)務開發(fā)中不只是將路由配置放到文件中,而是需要進行動態(tài)管理并且可以在變化時不用重啟系統(tǒng)就可以更新。與此同時還需要在接口訪問的時候,可以增加一些權限驗證以防止惡意訪問。
1.Filter過濾器,通過繼承實現(xiàn)對應方法可以進行控制過濾;
PRE
:這種過濾器在請求被路由之前調用。我們可利用這種過濾器實現(xiàn)身份驗證、在集群中選擇請求的微服務、記錄調試信息等。ROUTING
:這種過濾器將請求路由到微服務。這種過濾器用于構建發(fā)送給微服務的請求,并使用 Apache HttpClient 或 Netfilx Ribbon 請求微服務。POST
:這種過濾器在路由到微服務以后執(zhí)行。這種過濾器可用來為響應添加標準的 HTTP Header、收集統(tǒng)計信息和指標、將響應從微服務發(fā)送給客戶端等。ERROR
:在其他階段發(fā)生錯誤時執(zhí)行該過濾器。 除了默認的過濾器類型,Zuul 還允許我們創(chuàng)建自定義的過濾器類型。例如,我們可以定制一種 STATIC 類型的過濾器,直接在 Zuul 中生成響應,而不將請求轉發(fā)到后端的微服務。
2.自定義路由,同構實現(xiàn)SimpleRouteLocator和RefreshableRouteLocator自動刷新
- protected Map<String, ZuulRoute> locateRoutes():此方法是加載路由配置的,父類中是獲取properties中的路由配置,可以通過擴展此方法,達到動態(tài)獲取配置的目的
- public Route getMatchingRoute(String path):此方法是根據(jù)訪問路徑,獲取匹配的路由配置,父類中已經匹配到路由,可以通過路由id查找自定義配置的路由規(guī)則,以達到根據(jù)自定義規(guī)則動態(tài)分流的效果
環(huá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 & 動態(tài)路由與權限過濾
- 1.通過RouteLocator實現(xiàn)自己的動態(tài)路由配置,其實就是把配置文件內容轉移到這里用代碼類實現(xiàn),并且可以根據(jù)需要修改為從數(shù)據(jù)庫里獲取。
- 2.TokenFilter提供了權限驗證功能,當用戶訪問時候會帶上token否則攔截
- 3.此外還提供了自動刷新的接口,用于外部調用刷新配置
- 4.最后我們需要修改application配置,zuul中還需要排除不做路由的接口[刷新權限接口]
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 & 權限校驗類
public class TokenFilter extends ZuulFilter { /** * 過濾器的類型,它決定過濾器在請求的哪個生命周期中執(zhí)行。 * FilterConstants.PRE_TYPE:代表會在請求被路由之前執(zhí)行。 * PRE、ROUTING、POST、ERROR */ public String filterType() { return FilterConstants.PRE_TYPE; } /** * filter執(zhí)行順序,通過數(shù)字指定。[數(shù)字越大,優(yōu)先級越低] */ public int filterOrder() { return 0; } /** * 判斷該過濾器是否需要被執(zhí)行。這里我們直接返回了true,因此該過濾器對所有請求都會生效。 * 實際運用中我們可以利用該函數(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 配置組內容 */ 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"); // 如果使用了注冊中心,那么可以根據(jù)serviceId進行訪問。 // 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 & 路由刷新服務
@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 & 啟動服務注意注解,另外提供了服務接口
@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/ # 動態(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
測試驗證
1.分別啟動如下服務;
- itstack-demo-springcloud-eureka-server 服務注冊與發(fā)現(xiàn)
- itstack-demo-springcloud-eureka-client 接口提供方
- itstack-demo-springcloud-hystrix-feign 調用端
- itstack-demo-springcloud-hystrix-ribbon 調用端
- itstack-demo-springcloud-zuul 路由服務
2.可測試接口列表;
- 路由服務: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
- 內容配置:http://localhost:10001/api/queryRouteInfo
綜上總結
路由服務可以方便的幫我們控制業(yè)務類型的區(qū)分訪問,同時自動刷新可以更加方便的使用網關路由
權限驗證是幾乎不可少的在實際開發(fā)過程中會經常用到,所有的接口必須是安全可靠的,保證數(shù)據(jù)不泄露
另外還可以考慮從入?yún)⒌挠脩羯矸葸M行路由,這樣可以把數(shù)據(jù)庫路由提前,讓不同用戶組直接訪問到不同的數(shù)據(jù)庫組
以上為個人經驗,希望能給大家一個參考,也希望大家多多支持腳本之家。
相關文章
Spring Boot集成MyBatis訪問數(shù)據(jù)庫的方法
這篇文章主要介紹了Spring Boot集成MyBatis訪問數(shù)據(jù)庫的方法,需要的朋友可以參考下2017-04-04kafka運維consumer-groups.sh消費者組管理
這篇文章主要為大家介紹了kafka運維consumer-groups.sh消費者組管理,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進步,早日升職加薪2022-11-11Java集合之Set、HashSet、LinkedHashSet和TreeSet深度解析
這篇文章主要介紹了Java集合之Set、HashSet、LinkedHashSet和TreeSet深度解析,List是有序集合的根接口,Set是無序集合的根接口,無序也就意味著元素不重復,更嚴格地說,Set集合不包含一對元素e1和e2 ,使得e1.equals(e2) ,并且最多一個空元素,需要的朋友可以參考下2023-09-09