微服务中Zuul服务网关一共定义了四种类型的过滤器:
- pre:在请求被路由(转发)之前调用
- route:在路由(请求)转发时被调用
- error:服务网关发生异常时被调用
- post:在路由(转发)请求后调用
我在项目中用到了,pre/error/post三种类型,先记录一下
pre过滤器主要是用来校验各种信息的
import com.alibaba.fastjson.JSONObject;
import com.dkjk.gateway.context.ResponseBean;
import com.dkjk.gateway.domain.DockCompanyService;
import com.dkjk.gateway.domain.UserService;
import com.netflix.zuul.ZuulFilter;
import com.netflix.zuul.context.RequestContext;
import com.netflix.zuul.exception.ZuulException;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;
import org.springframework.stereotype.Component;
import org.springframework.web.bind.annotation.RequestMethod;
import javax.annotation.Resource;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.io.PrintWriter;
/**
* @author: qjc
* @createTime: 2019/4/13 16:08
* @Description: 接口安全验证过滤器
*/
@Component
@Slf4j
public class ValidFilter extends ZuulFilter {
@Override
public String filterType() {
return "pre";
}
@Override
public int filterOrder() {
return 0;
}
@Override
public boolean shouldFilter() {
// 进行跨域请求的时候,并且请求头中有额外参数,比如token,客户端会先发送一个OPTIONS请求来探测后续需要发起的跨域POST请求是否安全可接受
// 所以这个请求就不需要拦截,下面是处理方式
RequestContext requestContext = RequestContext.getCurrentContext();
HttpServletRequest request = requestContext.getRequest();
if (request.getMethod().equals(RequestMethod.OPTIONS.name())) {
log.info("OPTIONS请求不做拦截操作");
return false;
}
return true;
}
@Override
public Object run() throws ZuulException {
RequestContext requestContext = RequestContext.getCurrentContext();
HttpServletRequest request = requestContext.getRequest();
String userToken = request.getHeader("apikey");
if (StringUtils.isBlank(userToken)) {
log.warn("apikey为空");
sendError(requestContext, 99001, "请传输参数apikey");
return null;
}
return null;
}
/**
* 发送错误消息
*
* @param requestContext
* @param status
* @param msg
*/
private void sendError(RequestContext requestContext, int status, String msg) {
//过滤该请求,不往下级服务转发,到此结束不进行路由
requestContext.setSendZuulResponse(false);
HttpServletResponse response = requestContext.getResponse();
response.setHeader("Content-type", "application/json;charset=UTF-8");
response.setCharacterEncoding("UTF-8");
PrintWriter pw = null;
try {
pw = response.getWriter();
pw.write(JSONObject.toJSONString(new ResponseBean(status, msg, null)));
} catch (IOException e) {
log.error(e.getMessage());
} finally {
pw.close();
}
}
}
post过滤器可以在请求转发后获取请求信息和响应入库,或者日志记录
import com.alibaba.fastjson.JSON;
import com.netflix.zuul.ZuulFilter;
import com.netflix.zuul.context.RequestContext;
import com.netflix.zuul.exception.ZuulException;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.io.IOUtils;
import org.apache.commons.lang3.StringUtils;
import org.springframework.stereotype.Component;
import org.springframework.web.bind.annotation.RequestMethod;
import javax.servlet.http.HttpServletRequest;
import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStream;
import java.util.Enumeration;
import java.util.HashMap;
import java.util.Map;
/**
* @author: qjc
* @createTime: 2019/5/6 11:07
* @Description:
*/
@Component
@Slf4j
public class ResponseFilter extends ZuulFilter {
@Override
public String filterType() {
return "post";
}
@Override
public int filterOrder() {
return 2;
}
@Override
public boolean shouldFilter() {
// 进行跨域请求的时候,并且请求头中有额外参数,比如token,客户端会先发送一个OPTIONS请求来探测后续需要发起的跨域POST请求是否安全可接受
// 所以这个请求就不需要拦截,下面是处理方式
RequestContext requestContext = RequestContext.getCurrentContext();
HttpServletRequest request = requestContext.getRequest();
if (request.getMethod().equals(RequestMethod.OPTIONS.name())) {
log.info("OPTIONS请求不做拦截操作");
return false;
}
// 如果前面的拦截器不进行路由,那么后面的过滤器就没必要执行
if (!requestContext.sendZuulResponse()) {
return false;
}
return true;
}
@Override
public Object run() throws ZuulException {
RequestContext requestContext = RequestContext.getCurrentContext();
InputStream stream = requestContext.getResponseDataStream();
if (stream == null) {
return null;
}
HttpServletRequest request = requestContext.getRequest();
String requestParams = getRequestParams(requestContext, request);
System.err.println(requestParams);
try {
String responseBoby = IOUtils.toString(stream);
RequestContext.getCurrentContext().setResponseBody(responseBoby);
} catch (IOException e) {
e.printStackTrace();
}
return null;
}
//获取请求参数,适用于POST请求/GET请求,以及参数拼接在URL后面的POST请求
private String getRequestParams(RequestContext requestContext, HttpServletRequest request) {
String requestParams = null;
String requestMethod = request.getMethod();
StringBuilder params = new StringBuilder();
Enumeration<String> names = request.getParameterNames();
if (requestMethod.equals("GET")) {
while (names.hasMoreElements()) {
String name = (String) names.nextElement();
params.append(name);
params.append("=");
params.append(request.getParameter(name));
params.append("&");
}
requestParams = params.delete(params.length() - 1, params.length()).toString();
} else {
Map<String, String> res = new HashMap<>();
Enumeration<?> temp = request.getParameterNames();
if (null != temp) {
while (temp.hasMoreElements()) {
String en = (String) temp.nextElement();
String value = request.getParameter(en);
res.put(en, value);
}
requestParams = JSON.toJSONString(res);
}
if (StringUtils.isBlank(requestParams) || "{}".equals(requestParams)) {
BufferedReader br = null;
StringBuilder sb = new StringBuilder("");
try {
br = request.getReader();
String str;
while ((str = br.readLine()) != null) {
sb.append(str);
}
br.close();
} catch (IOException e) {
e.printStackTrace();
} finally {
if (null != br) {
try {
br.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
requestParams = sb.toString();
}
}
return requestParams;
}
}
error过滤器是在服务网关出现异常的时候起作用的
import com.alibaba.fastjson.JSONObject;
import com.dkjk.gateway.context.ResponseBean;
import com.netflix.zuul.ZuulFilter;
import com.netflix.zuul.context.RequestContext;
import com.netflix.zuul.exception.ZuulException;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Component;
import org.springframework.util.ReflectionUtils;
import javax.servlet.http.HttpServletResponse;
import java.io.PrintWriter;
/**
* @author: qjc
* @createTime: 2019/5/30 19:11
* @Description: 处理请求发生错误时过滤器
*/
@Component
@Slf4j
public class ErrorFilter extends ZuulFilter {
@Override
public String filterType() {
return "error";
}
@Override
public int filterOrder() {
//需要在默认的 SendErrorFilter 之前
return -1;
}
@Override
public boolean shouldFilter() {
// 只有在抛出异常时才会进行拦截
return RequestContext.getCurrentContext().containsKey("throwable");
}
@Override
public Object run() {
try {
RequestContext requestContext = RequestContext.getCurrentContext();
Object e = requestContext.get("throwable");
if (e != null && e instanceof ZuulException) {
ZuulException zuulException = (ZuulException) e;
// 删除该异常信息,不然在下一个过滤器中还会被执行处理
requestContext.remove("throwable");
// 响应给客户端信息
HttpServletResponse response = requestContext.getResponse();
response.setHeader("Content-type", "application/json;charset=UTF-8");
response.setCharacterEncoding("UTF-8");
PrintWriter pw = null;
pw = response.getWriter();
pw.write(JSONObject.toJSONString(new ResponseBean(99999, "系统出现异常", null)));
pw.close();
}
} catch (Exception ex) {
log.error("Exception filtering in custom error filter", ex);
ReflectionUtils.rethrowRuntimeException(ex);
}
return null;
}
}
来源:oschina
链接:https://my.oschina.net/u/4397772/blog/3517666