服务网关ZuulFilter过滤器--pre/post/error的用法(校验和获取路由后的请求/响应信息,处理服务网关异常)

微服务中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;
    }
}

 

上一篇:SSTI题目整理(未完)


下一篇:BUUCTF:[BJDCTF2020]The mystery of ip---------SSTI