Spring Cloud Zuul 服务网关(过滤器)
写在前面 本文参考以下文章,请参考原文
Spring Cloud构建微服务架构:服务网关(过滤器)【Dalston版】
1.微服务架构中 对外服务的权限控制
通过网关的路由功能,我们可以实现微服务应用提供的接口就可以通过统一的API网关入口被客户端访问到了。
但是,每个客户端用户请求微服务应用提供的接口时,它们的访问权限往往都需要有一定的限制,系统并不会将所有的微服务接口都对它们开放。现在的网关并没有这样的功能。
为了实现对客户端请求的安全校验和权限控制,可以为每个微服务应用都实现一套用于校验签名和鉴别权限的过滤器或拦截器。不过,这样的做法并不可取,会有很多冗余代码,并且开发维护的工作量很大。
所以,比较好的做法是将这些校验逻辑剥离出去,构建出一个独立的鉴权服务。然后直接在微服务应用中通过调用鉴权服务来实现校验,但是这样的做法仅仅只是解决了鉴权逻辑的分离,并没有在本质上将这部分不属于业务的逻辑拆分出原有的微服务应用,冗余的拦截器或过滤器依然会存在。
更好的做法是通过前置的网关服务来完成这些非业务性质的校验。由于网关服务的加入,外部客户端访问我们的系统已经有了统一入口,我们可以在请求到达的时候就完成 与具体业务无关校验和过滤,而不是转发后再过滤而导致更长的请求延迟。这样微服务就可以去除各种复杂的过滤器和拦截器,接口的开发和测试复杂度也会降低。
2. Spring Cloud Zuul 过滤器功能
我们可以使用Spring Cloud Zuul的过滤器功能,来实现 在API网关中定义过滤器来实现对外部客户请求的拦截,过滤,校验。具体实现方法:继承ZuulFilter抽象类并实现它定义的四个抽象函数就可以完成对请求的拦截和过滤了。
step1:用网关的路由功能用到的服务注册中心eureka-server , 网关内部的微服务 eureka-client 和 eureka-consumer,网关api-gateway
step2:在网关中添加过滤器,实现外部请求中含有参数accessToken的才可以校验通过,否则返回401
import javax.servlet.http.HttpServletRequest; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import com.netflix.zuul.ZuulFilter; import com.netflix.zuul.context.RequestContext; public class AccessFilter extends ZuulFilter { private static Logger log = LoggerFactory.getLogger(AccessFilter.class); @Override public String filterType() { return "pre"; } @Override public int filterOrder() { return 0; } @Override public boolean shouldFilter() { return true; } @Override public Object run() { RequestContext ctx = RequestContext.getCurrentContext(); HttpServletRequest request = ctx.getRequest(); log.info("send {} request to {}", request.getMethod(), request.getRequestURL().toString()); Object accessToken = request.getParameter("accessToken"); if(accessToken == null) { log.warn("access token is empty"); ctx.setSendZuulResponse(false); ctx.setResponseStatusCode(401); return null; } log.info("access token ok"); return null; } }
step3:注册过滤器 到网关微服务的配置类
@Bean public AccessFilter accessFilter() { return new AccessFilter(); }
step4:重启api-gateway服务,发起以下请求,查看请求结果来验证我们定义的过滤器是否起作用
http://localhost:1101/eureka-client/discoveryClient :返回401错误
http://localhost:1101/eureka-client/discoveryClient?accessToken=token :路由到eureka-client微服务的/discoveryClient接口
3. Spring Cloud Zuul ZuulFilter抽象类
在上面实现的过滤器代码中,我们通过继承ZuulFilter抽象类并重写了下面的四个方法来实现自定义的过滤器。这四个方法分别定义了:
- filterType:过滤器的类型,它决定过滤器在请求的哪个生命周期中执行。这里定义为
pre
,代表会在请求被路由之前执行。 - filterOrder:过滤器的执行顺序。当请求在一个阶段中存在多个过滤器时,需要根据该方法返回的值来依次执行。
- shouldFilter:判断该过滤器是否需要被执行。这里我们直接返回了true,因此该过滤器对所有请求都会生效。实际运用中我们可以利用该函数来指定过滤器的有效范围。
- run:过滤器的具体逻辑。这里我们通过 ctx.setSendZuulResponse(false);令zuul过滤该请求,不对其进行路由,然后通过ctx.setResponseStatusCode(401);设置了其返回的错误码,当然我们也可以进一步优化我们的返回,比如,通过ctx.setResponseBody(body);对返回body内容进行编辑等。