前言介绍
在实际的业务开发中不只是将路由配置放到文件中,而是需要进行动态管理并且可以在变化时不用重启系统就可以更新。与此同时还需要在接口访问的时候,可以增加一些权限验证以防止恶意访问。
-
Filter过滤器,通过继承实现对应方法可以进行控制过滤;
-
PRE:这种过滤器在请求被路由之前调用。我们可利用这种过滤器实现身份验证、在集群中选择请求的微服务、记录调试信息等。
-
ROUTING:这种过滤器将请求路由到微服务。这种过滤器用于构建发送给微服务的请求,并使用 Apache HttpClient 或 Netfilx Ribbon 请求微服务。
-
POST:这种过滤器在路由到微服务以后执行。这种过滤器可用来为响应添加标准的 HTTP Header、收集统计信息和指标、将响应从微服务发送给客户端等。
-
ERROR:在其他阶段发生错误时执行该过滤器。除了默认的过滤器类型,Zuul 还允许我们创建自定义的过滤器类型。例如,我们可以定制一种 STATIC 类型的过滤器,直接在 Zuul 中生成响应,而不将请求转发到后端的微服务。
-
自定义路由,同构实现SimpleRouteLocator和RefreshableRouteLocator自动刷新
- protected Map
locateRoutes():此方法是加载路由配置的,父类中是获取properties中的路由配置,可以通过扩展此方法,达到动态获取配置的目的 -
public Route getMatchingRoute(String path):此方法是根据访问路径,获取匹配的路由配置,父类中已经匹配到路由,可以通过路由id查找自定义配置的路由规则,以达到根据自定义规则动态分流的效果
环境准备
-
jdk 1.8、idea2018、Maven3
-
Spring Boot 2.0.6.RELEASE
-
Spring Cloud Finchley.SR2
代码示例
1itstack-demo-springcloud-08 2├── itstack-demo-springcloud-eureka-client 3│ └── src 4│ └── main 5│ ├── java 6│ │ └── org.itstack.demo 7│ │ ├── web 8│ │ │ └── EurekaClientController.java 9│ │ └── EurekaClientApplication.java 10│ └── resources 11│ └── application.yml 12├── itstack-demo-springcloud-eureka-server 13│ └── src 14│ └── main 15│ ├── java 16│ │ └── org.itstack.demo 17│ │ └── EurekaServerApplication.java 18│ └── resources 19│ └── application.yml 20├── itstack-demo-springcloud-hystrix-feign 21│ └── src 22│ └── main 23│ ├── java 24│ │ └── org.itstack.demo 25│ │ ├── service 26│ │ │ ├── hystrix 27│ │ │ │ └── FeignServiceHystrix.java 28│ │ │ └── FeignService.java 29│ │ ├── web 30│ │ │ └── FeignController.java 31│ │ └── FeignApplication.java 32│ └── resources 33│ └── application.yml 34├── itstack-demo-springcloud-hystrix-ribbon 35│ └── src 36│ └── main 37│ ├── java 38│ │ └── org.itstack.demo 39│ │ ├── service 40│ │ │ └── RibbonService.java 41│ │ ├── web 42│ │ │ └── RibbonController.java 43│ │ └── RibbonApplication.java 44│ └── resources 45│ └── application.yml 46└── itstack-demo-springcloud-zuul 47 └── src 48 └── main 49 ├── java 50 │ └── org.itstack.demo 51 │ ├── config 52 │ │ └── ZuulConfig.java 53 │ ├── filter 54 │ │ └── TokenFilter.java 55 │ ├── router 56 │ │ └── RouteLocator.java 57 │ ├── service 58 │ │ └── RefreshRouteService.java 59 │ └── ZuulApplication.java 60 └── resources 61 └── application.yml
完整代码欢迎关注公众号:bugstack虫洞栈 回复“SpringCloud专题”进行下载
itstack-demo-springcloud-zuul & 动态路由与权限过滤
- 通过RouteLocator实现自己的动态路由配置,其实就是把配置文件内容转移到这里用代码类实现,并且可以根据需要修改为从数据库里获取。
- TokenFilter提供了权限验证功能,当用户访问时候会带上token否则拦截
- 此外还提供了自动刷新的接口,用于外部调用刷新配置
- 最后我们需要修改application配置,zuul中还需要排除不做路由的接口[刷新权限接口]
config/ZuulConfig.java & 路由配置类
1/** 2 * 路由配置 3 * 微信公众号:bugstack虫洞栈 | 专注原创技术专题案例 4 * 论坛:http://bugstack.cn 5 * Create by 付政委 on @2019 6 */ 7@Configuration 8public class ZuulConfig { 9 10 @Autowired 11 private ZuulProperties zuulProperties; 12 @Autowired 13 private ServerProperties server; 14 15 @Bean 16 public RouteLocator routeLocator() { 17 return new RouteLocator(this.server.getServlet().getPath(), this.zuulProperties); 18 } 19 20}
filter/TokenFilter.java & 权限校验类
1/** 2 * 微信公众号:bugstack虫洞栈 | 专注原创技术专题案例 3 * 论坛:http://bugstack.cn 4 * Create by 付政委 on @2019 5 */ 6public class TokenFilter extends ZuulFilter { 7 8 /** 9 * 过滤器的类型,它决定过滤器在请求的哪个生命周期中执行。 10 * FilterConstants.PRE_TYPE:代表会在请求被路由之前执行。 11 * PRE、ROUTING、POST、ERROR 12 */ 13 public String filterType() { 14 return FilterConstants.PRE_TYPE; 15 } 16 17 /** 18 * filter执行顺序,通过数字指定。[数字越大,优先级越低] 19 */ 20 public int filterOrder() { 21 return 0; 22 } 23 24 /** 25 * 判断该过滤器是否需要被执行。这里我们直接返回了true,因此该过滤器对所有请求都会生效。 26 * 实际运用中我们可以利用该函数来指定过滤器的有效范围。 27 */ 28 public boolean shouldFilter() { 29 return true; 30 } 31 32 /* 33 * 具体执行逻辑 34 */ 35 public Object run() { 36 RequestContext ctx = RequestContext.getCurrentContext(); 37 HttpServletRequest request = ctx.getRequest(); 38 String token = request.getParameter("token"); 39 if (token == null || token.isEmpty()) { 40 ctx.setSendZuulResponse(false); 41 ctx.setResponseStatusCode(401); 42 ctx.setResponseBody("refuse! token is empty"); 43 } 44 return null; 45 } 46 47}
router/RouteLocator.java & 路由类
1/** 2 * 微信公众号:bugstack虫洞栈 | 专注原创技术专题案例 3 * 论坛:http://bugstack.cn 4 * Create by 付政委 on @2019 5 */ 6public class RouteLocator extends SimpleRouteLocator implements RefreshableRouteLocator { 7 8 private ZuulProperties properties; 9 10 public RouteLocator(String servletPath, ZuulProperties properties) { 11 super(servletPath, properties); 12 this.properties = properties; 13 } 14 15 @Override 16 public void refresh() { 17 doRefresh(); 18 } 19 20 @Override 21 protected Map<String, ZuulRoute> locateRoutes() { 22 LinkedHashMap<String, ZuulRoute> routesMap = new LinkedHashMap<String, ZuulRoute>(); 23 //从application.properties中加载路由信息 24 routesMap.putAll(super.locateRoutes()); 25 //从db中加载路由信息 26 routesMap.putAll(routesConfigGroup()); 27 //优化一下配置 28 LinkedHashMap<String, ZuulRoute> values = new LinkedHashMap<>(); 29 for (Map.Entry<String, ZuulRoute> entry : routesMap.entrySet()) { 30 String path = entry.getKey(); 31 // Prepend with slash if not already present. 32 if (!path.startsWith("/")) { 33 path = "/" + path; 34 } 35 if (StringUtils.hasText(this.properties.getPrefix())) { 36 path = this.properties.getPrefix() + path; 37 if (!path.startsWith("/")) { 38 path = "/" + path; 39 } 40 } 41 values.put(path, entry.getValue()); 42 } 43 return values; 44 } 45 46 /** 47 * 路由配置组,可以从数据库中读取 48 * 基本配置与写在文件中配置类似,如下; 49 * # routes: 50 * # api-a: 51 * # path: /route-a/** 52 * # serviceId: itstack-demo-springcloud-feign 53 * # api-b: 54 * # path: /route-b/** 55 * # serviceId: itstack-demo-springcloud-ribbon 56 * @return 配置组内容 57 */ 58 private Map<String, ZuulRoute> routesConfigGroup() { 59 Map<String, ZuulRoute> routes = new LinkedHashMap<>(); 60 61 ZuulRoute zuulRoute = new ZuulRoute(); 62 zuulRoute.setId("route-a"); 63 zuulRoute.setPath("/route-a/**"); 64 zuulRoute.setServiceId("itstack-demo-springcloud-feign"); 65 // 如果使用了注册中心,那么可以根据serviceId进行访问。 66 // zuulRoute.setUrl("http://localhost:9001"); 67 zuulRoute.setRetryable(false); 68 zuulRoute.setStripPrefix(true); 69 zuulRoute.setSensitiveHeaders(new HashSet<>()); 70 71 routes.put(zuulRoute.getPath(), zuulRoute); 72 73 return routes; 74 } 75 76}
service/RefreshRouteService.java & 路由刷新服务
1/** 2 * 微信公众号:bugstack虫洞栈 | 专注原创技术专题案例 3 * 论坛:http://bugstack.cn 4 * Create by 付政委 on @2019 5 */ 6@Service 7public class RefreshRouteService { 8 9 @Autowired 10 private ApplicationEventPublisher publisher; 11 12 @Autowired 13 private RouteLocator routeLocator; 14 15 public void refreshRoute() { 16 RoutesRefreshedEvent routesRefreshedEvent = new RoutesRefreshedEvent(routeLocator); 17 publisher.publishEvent(routesRefreshedEvent); 18 } 19 20}
ZuulApplication.java & 启动服务注意注解,另外提供了服务接口
1/** 2 * 微信公众号:bugstack虫洞栈 | 专注原创技术专题案例 3 * 论坛:http://bugstack.cn 4 * Create by 付政委 on @2019 5 */ 6@SpringBootApplication 7@EnableZuulProxy 8@EnableEurekaClient 9@EnableDiscoveryClient 10@RestController 11public class ZuulApplication { 12 13 public static void main(String[] args) { 14 SpringApplication.run(ZuulApplication.class, args); 15 } 16 17 @Bean 18 public TokenFilter tokenFilter() { 19 return new TokenFilter(); 20 } 21 22 @Autowired 23 private RefreshRouteService refreshRouteService; 24 @Autowired 25 private ZuulHandlerMapping zuulHandlerMapping; 26 27 @RequestMapping("api/refresh") 28 public String refresh(){ 29 refreshRouteService.refreshRoute(); 30 return "success"; 31 } 32 33 @RequestMapping("api/queryRouteInfo") 34 @ResponseBody 35 public Map<String, Object> queryRouteInfo(){ 36 return zuulHandlerMapping.getHandlerMap(); 37 } 38 39}
application.yml & 配置文件修改,路由过滤
1server: 2 port: 10001 3 4spring: 5 application: 6 name: itstack-demo-ddd-zuul 7 8eureka: 9 client: 10 serviceUrl: 11 defaultZone: http://localhost:7397/eureka/ 12 13# 动态路由,以下配置注释; 14# http://localhost:10001/route-a/api/queryUserInfo?userId=111 15# http://localhost:10001/route-b/api/queryUserInfo?userId=111 16zuul: 17 ignoredPatterns: /api/** 18# routes: 19# api-a: 20# path: /route-a/** 21# serviceId: itstack-demo-springcloud-feign 22# api-b: 23# path: /route-b/** 24# serviceId: itstack-demo-springcloud-ribbon 25
测试验证
-
分别启动如下服务;
-
itstack-demo-springcloud-eureka-server 服务注册与发现
-
itstack-demo-springcloud-eureka-client 接口提供方
-
itstack-demo-springcloud-hystrix-feign 调用端
-
itstack-demo-springcloud-hystrix-ribbon 调用端
-
itstack-demo-springcloud-zuul 路由服务
-
可测试接口列表;
- 路由服务:http://localhost:10001/route-a/api/queryUserInfo?userId=111&token=111
java<br /> Hi 微信公众号:bugstack虫洞栈 | 111 >: from eureka client port: 8001 From Feign<br />
-
刷新配置:http://localhost:10001/api/refresh
-
内容配置:http://localhost:10001/api/queryRouteInfo
综上总结
-
路由服务可以方便的帮我们控制业务类型的区分访问,同时自动刷新可以更加方便的使用网关路由
-
权限验证是几乎不可少的在实际开发过程中会经常用到,所有的接口必须是安全可靠的,保证数据不泄露
-
另外还可以考虑从入参的用户身份进行路由,这样可以把数据库路由提前,让不同用户组直接访问到不同的数据库组