目錄
相關知識:
在SpringCloud微服務中,Zuul除了最常用的(動態)路由功能外,還有很多其他的功能,如過濾、認證、服務遷移、壓力測試等等,今天主要介紹Zuul的過濾器。
默認情況下,Spring Cloud Zuul在請求路由時,會過濾掉請求頭信息中的 一些敏感信息,防止它們被傳遞到下游的外部服務器。 默認的敏感頭信息通過 zuul.sensitiveHeaders參數定義(可通過覆蓋的方式來設置),包括Cookie、Set-Cookie、Authorization 三個屬性。
注:可通過覆蓋的方式來設置。如指向過濾掉Cookie、Set-Cookie,不想過濾掉Authorization ,那麼只要需要
在application.properties文件中設置zuul.sensitiveHeaders = Cookie, Set-Cookie即可。
Zuul過濾器:
Zuul過濾器的使用方式:
使用方式很簡單,繼承ZuulFilter類,重寫相應的方法即可(可詳見本文末給出的使用示例)。
Zuul過濾器的類型:
-
pre:路由請求前過濾。
-
post:路由請求後(此時已經走完目標服務程序了)過濾。
-
route:路由請求時過濾。
-
error:當上述三種過濾器拋出異常時,會走error過濾。
注:如果目標服務拋出異常,並不會走error過濾器,error過濾器的“管轄範圍”只是pre、post、route這三種過濾器。
Zuul過濾器的調用順序(過濾優先級):
源碼ZuulServlet類(相關截圖):
上圖是四種Zuul過濾器的總體過濾順序,對於同一類型的過濾器,其過濾順序是由filterOrder方法(繼承ZuulFilter類時,需要重寫此方法)的返回值決定的,filterOrder返回值越小,在同一類型種,越先過濾,此處源碼可見FilterLoader類的以下部分:
Zuul調用目標服務的時機:
在Zuul的衆多route類型的過濾器中,RibbonRoutingFilter過濾的run()方法裏,實現了由Zuul路由到具體的服務:
Zuul過濾器簡單使用示例(以pre過濾器爲例):
import com.netflix.zuul.ZuulFilter;
import com.netflix.zuul.context.RequestContext;
import com.util.RequestBodyUtil;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.context.annotation.PropertySource;
import org.springframework.http.HttpStatus;
import org.springframework.stereotype.Component;
import javax.servlet.http.HttpServletRequest;
import java.util.Arrays;
import java.util.Random;
/**
* 使用ZuulFilter實現 簡單認證
*
* @author JustryDeng
* @date 2019/3/1 1:36
*/
@Component
@Slf4j
@PropertySource(value = {"classpath:/authe_info.properties"}, encoding="utf8")
public class PreFilter extends ZuulFilter {
@Value("${need-filter-uri}")
private String[] needFilterURIs;
/**
* 過濾類型
* 有:
* 【pre】路由請求前被調用過濾、
* 【post】後置過濾、
* 【error】錯誤過濾、
* 【route】路由請求時被調用
*
* @date 2019/3/19 14:49
*/
@Override
public String filterType() {
// 設置爲 前置過濾
return "pre";
}
/**
* 設置過濾優先級
*
* 注:當有多個同類型(即;同filterType)的ZuulFilter的子類時,可使用此返回值指定過濾器優先級;值越小越先過濾
*
* @date 2019/3/19 14:55
*/
@Override
public int filterOrder() {
return 0;
}
/**
* 設置 哪些請求 需要過濾
*
* @date 2019/3/19 14:55
*/
@Override
public boolean shouldFilter() {
RequestContext requestContext = RequestContext.getCurrentContext();
HttpServletRequest request = requestContext.getRequest();
String uri = request.getRequestURI();
// 當爲目標URI時,需要過濾
return Arrays.asList(needFilterURIs).contains(uri);
}
/**
* 實際邏輯邏輯
*
* 注:當RequestContext.setSendZuulResponse(false);時表示過濾失敗,zuul不對其進行路由
*/
@Override
public Object run() {
// 獲取請求上下文
RequestContext requestContext = RequestContext.getCurrentContext();
// 獲取請求
HttpServletRequest request = requestContext.getRequest();
if (doFiltrate(request)) {
// 驗證通過
return null;
}
// 如果驗證不通過,那麼過濾該請求,不往下級服務去轉發請求,到此結束
requestContext.setSendZuulResponse(false);
requestContext.setResponseStatusCode(HttpStatus.FORBIDDEN.value());
requestContext.setResponseBody(HttpStatus.FORBIDDEN.getReasonPhrase());
requestContext.getResponse().setContentType("text/html;charset=UTF-8");
return null;
}
/**
* 進行過濾
*
* @param request
* 請求
* @return 是否通過
* @author JustryDeng
* @date 2019/3/1 2:13
*/
private boolean doFiltrate (HttpServletRequest request) {
try {
/// 獲取請求頭
String who = request.getHeader("Authorization");
log.info(" requestHeader param 【Authorization】 is -> {} !", who);
/// 向請求頭中添加信息
// requestContext.addZuulRequestHeader("");
// 獲取請求體
RequestBodyUtil requestBodyUtil = new RequestBodyUtil(request);
String requestBody = requestBodyUtil.getBody();
log.info(" got requestBady -> {}", requestBody);
// TODO 由於是測試代碼,這裏隨機返回 成功、失敗
return new Random().nextBoolean();
} catch (Exception e) {
log.error(" zull authe occur error !", e);
return false;
}
}
}