前面学习了 Zuul 的一些特性和基本使用,包括请求转发,负载均衡等,前面也提到 Zuul 还可以用来做过滤权限、记录API日志等功能,其实这些都是基于 ZuulFilter 过滤器来实现的,今天对学习 ZuulFilter 做一个总结。
如何利用 ZuulFilter 实现权限控制?
Zuul 过滤器
首先看下官方的 Zuul 执行流程图
结合官方文档和流程图,得出Filter 是 Zuul 的核心,实现了对外部请求的控制。ZuulFilter 有四种,代表了 Zuul 中 filter 的四个生命周期
- PRE: 这种过滤器在请求被路由之前调用。我们可利用这种过滤器实现身份验证、在集群中选择请求的微服务、记录调试信息等。
- ROUTING:这种过滤器将请求路由到微服务。这种过滤器用于构建发送给微服务的请求,并使用Apache HttpClient或Netfilx Ribbon请求微服务。
- POST:这种过滤器在路由到微服务以后执行。这种过滤器可用来为响应添加标准的HTTP Header、收集统计信息和指标、将响应从微服务发送给客户端等。
- ERROR:在其他阶段发生错误时执行该过滤器。 除了默认的过滤器类型,Zuul还允许我们创建自定义的过滤器类型。例如,我们可以定制一种STATIC类型的过滤器,直接在Zuul中生成响应,而不将请求转发到后端的微服务。
我们可以自定义 Filter,来让 Zuul 具有我们想要的功能,zuul过滤器是链型。
代码示例
让 zuul 模拟登录功能,只有携带特定token标识的用户才可以执行登录。
继续需用之前的 eureka-server 和 eureka-provider 作为注册中心和服务提供者。
新建 SpringBoot 项目,名称为 zuul-filter
依赖
1 2 3 4 5 6 7 8
| <dependency> <groupId>org.springframework.cloud</groupId> <artifactId>spring-cloud-starter-netflix-eureka-server</artifactId> </dependency> <dependency> <groupId>org.springframework.cloud</groupId> <artifactId>spring-cloud-starter-netflix-zuul</artifactId> </dependency>
|
配置文件
1 2 3 4 5 6 7 8 9
| spring: application: name: zuul-test server: port: 8888 eureka: client: service-url: defaultZone: http://localhost:8000/eureka/
|
自定义Zuul过滤器类
CustomTokenFilter
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40
| public class CustomTokenFilter extends ZuulFilter {
@Override public String filterType() { return "pre"; }
@Override public int filterOrder() { return 0; }
@Override public boolean shouldFilter() { return true; }
@Override public Object run() throws ZuulException { RequestContext requestContext = RequestContext.getCurrentContext(); HttpServletRequest request = requestContext.getRequest(); String token = request.getParameter("token"); if(StringUtils.isNotBlank(token)){ requestContext.setSendZuulResponse(true); requestContext.setResponseStatusCode(200); requestContext.set("success",true); }else{ requestContext.setSendZuulResponse(false); requestContext.setResponseBody("no token!!"); requestContext.setResponseStatusCode(400); requestContext.set("success",false); } return null; } }
|
CustomLoginFilter
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43
| public class CustomLoginFilter extends ZuulFilter { @Override public String filterType() { return "pre"; }
@Override public int filterOrder() { return 1; }
@Override public boolean shouldFilter() { RequestContext requestContext = RequestContext.getCurrentContext(); Object successObj = requestContext.get("success"); if(successObj instanceof Boolean){ return (boolean)successObj; } return false; }
@Override public Object run() throws ZuulException { RequestContext requestContext = RequestContext.getCurrentContext(); HttpServletRequest request = requestContext.getRequest(); String username = request.getParameter("username"); String password = request.getParameter("password"); if("admin".equals(username) && "123456".equals(password)){ requestContext.setSendZuulResponse(true); requestContext.setResponseStatusCode(200); requestContext.set("success",true); }else{ requestContext.setSendZuulResponse(false); requestContext.setResponseBody("username or password is error!"); requestContext.setResponseStatusCode(400); requestContext.set("success",false); } return null; } }
|
启动类
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18
| @SpringBootApplication @EnableZuulProxy public class ZuulFilterApplication {
public static void main(String[] args) { SpringApplication.run(ZuulFilterApplication.class, args); }
@Bean public CustomTokenFilter tokenFilter(){ return new CustomTokenFilter(); }
@Bean public CustomLoginFilter loginFilter(){ return new CustomLoginFilter(); } }
|
这里写了两个过滤器,一个是判断 token 是否为空,只有当 token 不为空时才可以执行下一个过滤器,判断用户名和密码是否正确,正确才去访问 provider 服务的接口。
测试
启动 eureka-server,eureka-provider,zuul-filter 项目
浏览器输入http://localhost:8888/provider/hello,页面显示:
no token!!
浏览器输入http://localhost:8888/provider/hello?token=a,页面显示:
username or password is error!
浏览器输入http://localhost:8888/provider/hello?token=a&username=admin&password=123456,页面显示:
hello! Yakai! from provider
说明 Zuul 的执行过滤器链成功!
以上代码测试对 ZuulFilter 有了基本的应用,在实际开发中可根据业务需要,自定义各种 Filter 进行不同的业务处理、甚至可以自定义不限于“PRE”、“ROUTING”、“POST”、”ERROR”的过滤器类型。