Zuul的github:https://github.com/Netflix/zuul
一系列的过滤器是zuul的核心,zuul的很多功能都是通过过滤器来实现的。在zuul的世界里定义了四种标准过滤器类型,这四种过滤器分别对应着请求的生命周期:
pre:此种过滤器在请求被路由之前执行,显然这种过滤器可以用来过滤请求(白黑名单)、安全验证等;
routing:此种过滤器复制将请求路由到具体的微服务上;
post:此种过滤器在请求被路由到微服务之后执行,可以用来统计用户行为、响应客户端等;
error:如果上面三种过滤器发生了错误,则执行此过滤器。
此外还支持"static"类型的过滤器,用于直接在zuul中生成response。结合抽象类ZuulFilter里的filterType属性注解来看:
/**
* to classify a filter by type. Standard types in Zuul are "pre" for pre-routing filtering,
* "route" for routing to an origin, "post" for post-routing filters, "error" for error handling.
* We also support a "static" type for static responses see StaticResponseFilter.
* Any filterType made be created or added and run by calling FilterProcessor.runFilters(type)
*
* @return A String representing that type
*/
abstract public String filterType();
自定义zuul filter非常简单,只需要继承ZuulFilte即可,现在编译一个zuul过滤器来实现过滤IP,只允许白名单里的IP允许访问功能,这个功能还是很常见的,例如你的服务器A出于安全性考虑,只允许接受供应商B服务器发出的请求调用:
import com.google.gson.Gson;
import com.netflix.zuul.ZuulFilter;
import com.netflix.zuul.context.RequestContext;
import com.simons.cn.util.CommonEnum;
import com.simons.cn.util.CommonResult;
import lombok.extern.slf4j.Slf4j;
import javax.servlet.ServletOutputStream;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
/**
* 说明:自定义pre类型zuul过滤器,实现限过滤白名单IP功能
* Author:simonsfan
*/
@Slf4j
public class PreRequestZuulFilter extends ZuulFilter {
/**
* to classify a filter by type. Standard types in Zuul are "pre" for pre-routing filtering,
* "route" for routing to an origin, "post" for post-routing filters, "error" for error handling.
* We also support a "static" type for static responses see StaticResponseFilter.
* Any filterType made be created or added and run by calling FilterProcessor.runFilters(type)
*
* @return A String representing that type
*/
@Override
public String filterType() {
return "pre";
}
/**
* filterOrder() must also be defined for a filter. Filters may have the same filterOrder if precedence is not
* important for a filter. filterOrders do not need to be sequential.
*
* @return the int order of a filter
*/
@Override
public int filterOrder() {
return 1;
}
/**
* whether this filter works or not
*
* @return true:work ; false:zuulfilter not work
*/
@Override
public boolean shouldFilter() {
return true;
}
/**
* concrete zuulfilter logic,defined by yourself
*
* @return object which you need
*/
@Override
public Object run() {
RequestContext currentContext = RequestContext.getCurrentContext();
HttpServletRequest request = currentContext.getRequest();
HttpServletResponse response = currentContext.getResponse();
String remoteIp = getIpAddrAdvanced(request);
//实际白名单应该做成配置化,我这里写死仅为了模拟场景
if (!remoteIp.equals("10.200.10.159")) {
try {
outPut(response);
} catch (Exception e) {
log.error("pre-zuulfilter run method exception:{}", e);
}
}
return null;
}
/**
* 获取客户端ip
* @param request
* @return
*/
public static String getIpAddrAdvanced(HttpServletRequest request) {
String ip = request.getHeader("x-forwarded-for");
if (ip == null || ip.length() == 0 || "unknown".equalsIgnoreCase(ip)) {
ip = request.getHeader("Proxy-Client-IP");
}
if (ip == null || ip.length() == 0 || "unknown".equalsIgnoreCase(ip)) {
ip = request.getHeader("WL-Proxy-Client-IP");
}
if (ip == null || ip.length() == 0 || "unknown".equalsIgnoreCase(ip)) {
ip = request.getHeader("HTTP_CLIENT_IP");
}
if (ip == null || ip.length() == 0 || "unknown".equalsIgnoreCase(ip)) {
ip = request.getHeader("HTTP_X_FORWARDED_FOR");
}
if (ip == null || ip.length() == 0 || "unknown".equalsIgnoreCase(ip)) {
ip = request.getRemoteAddr();
}
if (ip != null && ip.indexOf(",") != -1) {
String[] ipWithMultiProxy = ip.split(",");
for (int i = 0; i < ipWithMultiProxy.length; ++i) {
String eachIpSegement = ipWithMultiProxy[i];
if (!"unknown".equalsIgnoreCase(eachIpSegement)) {
ip = eachIpSegement;
break;
}
}
}
return ip;
}
/**
* 输出结果
* @param response
* @throws IOException
*/
public void outPut(HttpServletResponse response) throws IOException {
response.setContentType("application/json;charset=utf-8");
ServletOutputStream out = null;
try {
out = response.getOutputStream();
String message = new Gson().toJson(CommonResult.success(CommonEnum.ILLEALREQUEST.getCode(), CommonEnum.ILLEALREQUEST.getMessage()));
out.write(message.getBytes("UTF-8"));
} catch (Exception e) {
log.error("pre-zuulfilter error:{}", e);
} finally {
out.flush();
out.close();
}
}
}
CommonEnum枚举(部分):
ILLEALREQUEST("1001","非法请求!");
重载的四个方法在方法头部均有注释。接着启动类中加入自定义zuul过滤器PreRequestZuulFilter实例
import com.simons.cn.bean.PreRequestZuulFilter;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.cloud.netflix.zuul.EnableZuulProxy;
import org.springframework.context.annotation.Bean;
@SpringBootApplication
@EnableZuulProxy //表示开启Zuul代理
public class ZuulGatewayApplication {
@Bean
public PreRequestZuulFilter preRequestZuulFilter(){
return new PreRequestZuulFilter();
}
public static void main(String[] args) {
SpringApplication.run(ZuulGatewayApplication.class,args);
}
}
启动zuul-gateway项目和user-provider-eureka、discovery-eureka三个项目,浏览器访问http://localhost:10010/user/getuserinfo?name=jack,效果如图:
可以看到,所有非指定的IP均不能访问此服务,pre类型过滤器已经生效。
由于SpringCloud为zuul定义了一些默认过滤器(并且启用了),如上截图,在某些场景下我们项目不需要这些过滤器,就要禁用这些filter。禁用也比较简单,直接在配置文件中:zuul.
zuul:
DebugFilter:
pre:
disable: true
可以打断点测试一下,亲测有效!
此文项目的github:https://github.com/simonsfan/SpringCloud.git