Zuul实现动态路由与权限过滤器方式

《Zuul实现动态路由与权限过滤器方式》文章介绍如何通过Zuul实现动态路由与权限验证,利用自定义过滤器和配置刷新机制,提升系统灵活性与安全性,确保接口访问可控且无需重启... ...

前言介绍

在实际的业务开发中不只是将路由配置放到文件中,而是需要进行动态管理并且可以在变化时不用重启系统就可以更新。与此同时还需要在接口访问的时候,可以增加一些权限验证以防止恶意访问。

1.Filter过滤器,通过继承实现对应方法可以进行控制过滤;

  • PRE:这种过滤器在请求被路由之前调用。我们可利用这种过滤器实现身份验证、在集群中选择请求的微服务、记录调试信息等。
  • ROUTING:这种过滤器将请求路由到微服务。这种过滤器用于构建发送给微服务的请求,并使用 Apache HttpClient 或 Netfilx Ribbon 请求微服务。
  • POST:这种过滤器在路由到微服务以后执行。这种过滤器可用来为响应添加标准的 HTTP Header、收集统计信息和指标、将响应从微服务发送给客户端等。
  • ERROR:在其他阶段发生错误时执行该过滤器。 除了默认的过滤器类型,Zuul 还允许我们创建自定义的过滤器类型。例如,我们可以定制一种 STATIC 类型的过滤器,直接在 Zuul 中生成响应,而不将请求转发到后端的微服务。

2.自定义路由,同构实现SimpleRouteLocator和RefreshableRouteLocator自动刷新

  • protected Map<String, ZuulRoute> locateRoutes():此方法是加载路由配置的,父类中是获取properties中的路由配置,可以通过扩展此方法,达到动态获取配置的目的
  • public Route getMatchingRoute(String path):此方法是根据访问路径,获取匹配的路由配置,父类中已经匹配到路由,可以通过路由id查找自定义配置的路由规则,以达到根据自定义规则动态分流的效果

环境准备

  • 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 & 动态路由与权限过滤

  • 1.通过RouteLocator实现自己的动态路由配置,其实就是把配置文件内容转移到这里用代码类实现,并且可以根据需要修改为从数据库里获取。
  • 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 {
/**
* 过滤器的类型编程客栈,它决定过滤器在请求的哪个生命周期中执行。
* FilterConstants.PRE_TYPE:代表会在请求被路由之前执行。
* PRE、ROUTING、POST、ERROR
*/
public String filterType() {
return FilterConstants.PRE_TYPE;
}
/**
* filter执行顺序,通过数字指定。[数字越大,优先级越低]
*/
public int filterOrder() {
return 0;
}
/**
* 判断该过滤器是否需要被执行。这里我们直接返回了true,因此该过滤器对所有请求都会生效。
* 实际运用中我们可以利用该函数来指定过滤器的有效范围。
*/
public boolean shouldFilter() {
return true;
}
/*
* 具体执行逻辑
*/
public Object run() {
RequestContext ctx = RequestContext.getCurrentContext();
HttpServletRequest request = ctx.getRequest();
String token = request.getParameter("token"python);
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());
//优化一下配置
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 (jsStringUtils.hasText(this.properties.getPrefix())) {
path = this.properties.getPrefix() + path;
if (!path.startsWith("/")) {
path = "/" + path;
}
}
values.put(path, entry.getValue());
}
return values;
}
/**
* 路由配置组,可以从数据库中读取
* 基本配置与写在文件中配置类似,如下;
* #  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");
// 如果使用了注册中心,那么可以根据serviceId进行访问。
// zuulRoute.setUrl("http://localhost:9001");
zuulRoute.setRetryable(false);
zuulRoute.setStripPrefix(true);
zuulRoute.setSensitiveHeaders(new HashSet<>());
routes.put(zuulRoute.getPath(), zuulRoute);
return routejss;
}
}

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/
# 动态路由,以下配置注释;
# 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 服务注册与发现
  • 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

综上总编程客栈

路由服务可以方便的帮我们控制业务类型的区分访问,同时自动刷新可以更加方便的使用网关路由

权限验证是几乎不可少的在实际开发过程中会经常用到,所有的接口必须是安全可靠的,保证数据不泄露

另外还可以考虑从入参的用户身份进行路由,这样可以把数据库路由提前,让不同用户组直接访问到不同的数据库组

以上为个人经验,希望能给大家一个参考,也希望大家多多支持编程客栈(www.cppcns.com)。

本文标题: Zuul实现动态路由与权限过滤器方式
本文地址: http://www.cppcns.com/ruanjian/java/716992.html
.
搜索