經過上面所述的兩篇咱們,咱們已經可以實現請求的路由功能,因此咱們的微服務應用提供的接口就能夠經過統一的API網關入口被客戶端訪問到了。可是,每一個客戶端用戶請求微服務應用提供的接口時,它們的訪問權限每每都須要有必定的限制,系統並不會將全部的微服務接口都對它們開放。然而,目前的服務路由並無限制權限這樣的功能,全部請求都會被毫無保留地轉發到具體的應用並返回結果,爲了實現對客戶端請求的安全校驗和權限控制,最簡單和粗暴的方法就是爲每一個微服務應用都實現一套用於校驗簽名和鑑別權限的過濾器或攔截器。不過,這樣的作法並不可取,它會增長往後的系統維護難度,由於同一個系統中的各類校驗邏輯不少狀況下都是大體相同或相似的,這樣的實現方式會使得類似的校驗邏輯代碼被分散到了各個微服務中去,冗餘代碼的出現是咱們不但願看到的。因此,比較好的作法是將這些校驗邏輯剝離出去,構建出一個獨立的鑑權服務。在完成了剝離以後,有很多開發者會直接在微服務應用中經過調用鑑權服務來實現校驗,可是這樣的作法僅僅只是解決了鑑權邏輯的分離,並無在本質上將這部分不屬於業餘的邏輯拆分出原有的微服務應用,冗餘的攔截器或過濾器依然會存在。html
對於這樣的問題,更好的作法是經過前置的網關服務來完成這些非業務性質的校驗。因爲網關服務的加入,外部客戶端訪問咱們的系統已經有了統一入口,既然這些校驗與具體業務無關,那何不在請求到達的時候就完成校驗和過濾,而不是轉發後再過濾而致使更長的請求延遲。同時,經過在網關中完成校驗和過濾,微服務應用端就能夠去除各類複雜的過濾器和攔截器了,這使得微服務應用的接口開發和測試複雜度也獲得了相應的下降。web
爲了在API網關中實現對客戶端請求的校驗,咱們將須要使用到Spring Cloud Zuul的另一個核心功能:過濾器。api
Zuul容許開發者在API網關上經過定義過濾器來實現對請求的攔截與過濾,實現的方法很是簡單,咱們只須要繼承ZuulFilter抽象類並實現它定義的四個抽象函數就能夠完成對請求的攔截和過濾了。安全
好比下面的代碼,咱們定義了一個簡單的Zuul過濾器,它實現了在請求被路由以前檢查HttpServletRequest中是否有accessToken參數,如有就進行路由,若沒有就拒絕訪問,返回401 Unauthorized錯誤。併發
public class AccessFilter extends ZuulFilter { private static Logger log = LoggerFactory.getLogger(AccessFilter.class); @Override public String filterType() { return "pre"; } @Override public int filterOrder() { return 0; } @Override public boolean shouldFilter() { return true; } @Override public Object run() { RequestContext ctx = RequestContext.getCurrentContext(); HttpServletRequest request = ctx.getRequest(); log.info("send {} request to {}", request.getMethod(), request.getRequestURL().toString()); Object accessToken = request.getParameter("accessToken"); if(accessToken == null) { log.warn("access token is empty"); ctx.setSendZuulResponse(false); ctx.setResponseStatusCode(401); return null; } log.info("access token ok"); return null; } }
在上面實現的過濾器代碼中,咱們經過繼承ZuulFilter
抽象類並重寫了下面的四個方法來實現自定義的過濾器。這四個方法分別定義了:ide
filterType
:過濾器的類型,它決定過濾器在請求的哪一個生命週期中執行。這裏定義爲pre
,表明會在請求被路由以前執行。filterOrder
:過濾器的執行順序。當請求在一個階段中存在多個過濾器時,須要根據該方法返回的值來依次執行。shouldFilter
:判斷該過濾器是否須要被執行。這裏咱們直接返回了true
,所以該過濾器對全部請求都會生效。實際運用中咱們能夠利用該函數來指定過濾器的有效範圍。run
:過濾器的具體邏輯。這裏咱們經過ctx.setSendZuulResponse(false)
令zuul過濾該請求,不對其進行路由,而後經過ctx.setResponseStatusCode(401)
設置了其返回的錯誤碼,固然咱們也能夠進一步優化咱們的返回,好比,經過ctx.setResponseBody(body)
對返回body內容進行編輯等。在實現了自定義過濾器以後,它並不會直接生效,咱們還須要爲其建立具體的Bean才能啓動該過濾器,好比,在應用主類中增長以下內容:函數
@EnableZuulProxy @SpringCloudApplication public class Application { public static void main(String[] args) { new SpringApplicationBuilder(Application.class).web(true).run(args); } @Bean public AccessFilter accessFilter() { return new AccessFilter(); } }
在對api-gateway
服務完成了上面的改造以後,咱們能夠從新啓動它,併發起下面的請求,對上面定義的過濾器作一個驗證:微服務
http://localhost:1101/api-a/hello
:返回401錯誤http://localhost:1101/api-a/hello&accessToken=token
:正確路由到hello-service
的/hello
接口,並返回Hello World
到這裏,對於Spring Cloud Zuul過濾器的基本功能就以介紹完畢。讀者能夠根據本身的須要在服務網關上定義一些與業務無關的通用邏輯實現對請求的過濾和攔截,好比:簽名校驗、權限校驗、請求限流等功能。源碼來源測試