我们知道在引入了Zuul作为Api网关服务后,它可以做到请求路由和负载均衡等功能,具体实现可以参考《zuul的简单实现》这篇博客
这时问题来了,难道我们要对所有的请求的进行正常的路由么,要是他是非法请求怎么办,这时我们就需要用到Zuul的请求过滤功能,根据我们自己的合法逻辑对经过Api服务网关的请求进行校验,决定是返回响应错误还是正常路由。
项目结构介绍:
从注册中心可以看到有3个服务实例:
API-GATEWAY
EUREKA-CLIENT
EUREKA-FEIGN-CLIENT
API-GATEWAY配置文件信息如下:
spring:
application:
name: api-gateway
server:
port: 8766
zuul:
routes:
hello-feign:
path: /hello-feign/**
serviceId: eureka-feign-client
hello-eureka:
path: /hello-eureka/**
serviceId: eureka-client
eureka:
client:
service-url:
defaultZone: http://localhost:8761/eureka/
可以看到这里对 /hello-feign/** 和 /hello-eureka/**两个路径的请求配置了路由,采用的是服务路由。
另外两个服务的接口代码我就不贴了,直接描述下没加过滤器时的请求场景:
浏览器访问http://localhost:8766/hello-eureka/helloWorld/AA,这个请求时会经过Api网关Zuul的,结果如下:
接下里我们对Zuul的请求过滤功能做一个简单的实现,现在登陆一般都会校验token,所以就做一个token不为空,才正常路由的过滤功能。
一.写一个登陆过滤器LoginFilter,继承ZuulFilter类
package com.example.apigateway;
import com.netflix.zuul.ZuulFilter;
import com.netflix.zuul.ZuulFilterResult;
import com.netflix.zuul.context.RequestContext;
import com.netflix.zuul.exception.ZuulException;
import javax.servlet.http.HttpServletRequest;
import java.util.Objects;
public class LoginFilter extends ZuulFilter {
public LoginFilter() {
super();
}
@Override
public boolean isStaticFilter() {
return super.isStaticFilter();
}
@Override
public String disablePropertyName() {
return super.disablePropertyName();
}
@Override
public boolean isFilterDisabled() {
return super.isFilterDisabled();
}
@Override
public ZuulFilterResult runFilter() {
return super.runFilter();
}
@Override
public int compareTo(ZuulFilter filter) {
return super.compareTo(filter);
}
@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 httpServletRequest = requestContext.getRequest();
Object token = httpServletRequest.getParameter("token");
if (Objects.isNull(token)) {
requestContext.setSendZuulResponse(false);
//防止返回给前端时中文乱码
requestContext.getResponse().setContentType("text/html;charset=UTF-8");
requestContext.setResponseBody("当前状态未登录,请重新登录");
requestContext.setResponseStatusCode(401);
return null;
}
return null;
}
}
从代码里可看出我们重写了filterType() ,filterOrder(),shouldFilter(),run()4个方法,这里解释下每个方法的作用:
filterType:这是过滤器的类型,决定过滤器在请求的那个生命周期会被执行,这里我设置成"pre",代表着过滤器在路由前执行。
filterOrder:过滤器的执行顺序,有时一个请求可能要执行多次过滤,这个方法值得返回值就决定了这过滤器的执行顺序,filterType相同的情况下,数字越小越先执行,负数也是一样。
shouldFilter:决定要不要执行过滤器,true则是要执行,false则是不要
run:过滤器执行的具体逻辑。 requestContext.setSendZuulResponse(false)这句很关键,设置成false后,会让Zuul过滤了该请求,不对这个请求进行路由。
二.加上过滤器配置类,启动时加载,让过滤器生效
@Configuration
public class Filter {
@Bean
public LoginFilter loginFilter() {
return new LoginFilter();
}
}
这时我们预想中的结果是,只要请求中没带token就会返回"当前状态未登录,请重新登录"
测试下试试:
浏览器访问http://localhost:8766/hello-eureka/helloWorld/AA,结果如下:
加上token参数后,浏览器访问http://localhost:8766/hello-eureka/helloWorld/AA?token=1,结果如下:
对请求成功过滤。