微服務中Zuul服務網關一共定義了四種類型的過濾器:java
我在項目中用到了,pre/error/post三種類型,先記錄一下web
pre過濾器主要是用來校驗各類信息的spring
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過濾器能夠在請求轉發後獲取請求信息和響應入庫,或者日誌記錄apache
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過濾器是在服務網關出現異常的時候起做用的json
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; } }