Spring Cloud構建微服務架構:服務網(wǎng)關(過濾器)【Dalston版】

在前兩篇文章:服務網(wǎng)關(基礎)身冀、服務網(wǎng)關(路由配置)中,我們了解了Spring Cloud Zuul作為網(wǎng)關所具備的最基本功能:路由括享。本文我們將具體介紹一下Spring Cloud Zuul的另一項核心功能:過濾器搂根。

過濾器的作用

通過上面所述的兩篇我們,我們已經(jīng)能夠?qū)崿F(xiàn)請求的路由功能铃辖,所以我們的微服務應用提供的接口就可以通過統(tǒng)一的API網(wǎng)關入口被客戶端訪問到了剩愧。但是,每個客戶端用戶請求微服務應用提供的接口時娇斩,它們的訪問權限往往都需要有一定的限制仁卷,系統(tǒng)并不會將所有的微服務接口都對它們開放穴翩。然而,目前的服務路由并沒有限制權限這樣的功能锦积,所有請求都會被毫無保留地轉(zhuǎn)發(fā)到具體的應用并返回結果芒帕,為了實現(xiàn)對客戶端請求的安全校驗和權限控制,最簡單和粗暴的方法就是為每個微服務應用都實現(xiàn)一套用于校驗簽名和鑒別權限的過濾器或攔截器丰介。不過背蟆,這樣的做法并不可取,它會增加日后的系統(tǒng)維護難度哮幢,因為同一個系統(tǒng)中的各種校驗邏輯很多情況下都是大致相同或類似的淆储,這樣的實現(xiàn)方式會使得相似的校驗邏輯代碼被分散到了各個微服務中去,冗余代碼的出現(xiàn)是我們不希望看到的家浇。所以本砰,比較好的做法是將這些校驗邏輯剝離出去,構建出一個獨立的鑒權服務钢悲。在完成了剝離之后点额,有不少開發(fā)者會直接在微服務應用中通過調(diào)用鑒權服務來實現(xiàn)校驗,但是這樣的做法僅僅只是解決了鑒權邏輯的分離莺琳,并沒有在本質(zhì)上將這部分不屬于業(yè)余的邏輯拆分出原有的微服務應用还棱,冗余的攔截器或過濾器依然會存在。

對于這樣的問題惭等,更好的做法是通過前置的網(wǎng)關服務來完成這些非業(yè)務性質(zhì)的校驗珍手。由于網(wǎng)關服務的加入,外部客戶端訪問我們的系統(tǒng)已經(jīng)有了統(tǒng)一入口辞做,既然這些校驗與具體業(yè)務無關琳要,那何不在請求到達的時候就完成校驗和過濾,而不是轉(zhuǎn)發(fā)后再過濾而導致更長的請求延遲秤茅。同時稚补,通過在網(wǎng)關中完成校驗和過濾,微服務應用端就可以去除各種復雜的過濾器和攔截器了框喳,這使得微服務應用的接口開發(fā)和測試復雜度也得到了相應的降低课幕。

為了在API網(wǎng)關中實現(xiàn)對客戶端請求的校驗,我們將需要使用到Spring Cloud Zuul的另外一個核心功能:過濾器五垮。

Zuul允許開發(fā)者在API網(wǎng)關上通過定義過濾器來實現(xiàn)對請求的攔截與過濾乍惊,實現(xiàn)的方法非常簡單,我們只需要繼承ZuulFilter抽象類并實現(xiàn)它定義的四個抽象函數(shù)就可以完成對請求的攔截和過濾了放仗。

過濾器的實現(xiàn)

比如下面的代碼润绎,我們定義了一個簡單的Zuul過濾器,它實現(xiàn)了在請求被路由之前檢查HttpServletRequest中是否有accessToken參數(shù),若有就進行路由凡橱,若沒有就拒絕訪問小作,返回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;
    }

}

在上面實現(xiàn)的過濾器代碼中稼钩,我們通過繼承ZuulFilter抽象類并重寫了下面的四個方法來實現(xiàn)自定義的過濾器顾稀。這四個方法分別定義了:

  • filterType:過濾器的類型,它決定過濾器在請求的哪個生命周期中執(zhí)行坝撑。這里定義為pre静秆,代表會在請求被路由之前執(zhí)行。
  • filterOrder:過濾器的執(zhí)行順序巡李。當請求在一個階段中存在多個過濾器時抚笔,需要根據(jù)該方法返回的值來依次執(zhí)行。
  • shouldFilter:判斷該過濾器是否需要被執(zhí)行侨拦。這里我們直接返回了true殊橙,因此該過濾器對所有請求都會生效。實際運用中我們可以利用該函數(shù)來指定過濾器的有效范圍狱从。
  • run:過濾器的具體邏輯膨蛮。這里我們通過ctx.setSendZuulResponse(false)令zuul過濾該請求,不對其進行路由季研,然后通過ctx.setResponseStatusCode(401)設置了其返回的錯誤碼敞葛,當然我們也可以進一步優(yōu)化我們的返回,比如与涡,通過ctx.setResponseBody(body)對返回body內(nèi)容進行編輯等惹谐。

在實現(xiàn)了自定義過濾器之后,它并不會直接生效驼卖,我們還需要為其創(chuàng)建具體的Bean才能啟動該過濾器氨肌,比如,在應用主類中增加如下內(nèi)容:

@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服務完成了上面的改造之后款慨,我們可以重新啟動它儒飒,并發(fā)起下面的請求,對上面定義的過濾器做一個驗證:

  • http://localhost:1101/api-a/hello:返回401錯誤
  • http://localhost:1101/api-a/hello&accessToken=token:正確路由到hello-service/hello接口檩奠,并返回Hello World

到這里,對于Spring Cloud Zuul過濾器的基本功能就以介紹完畢附帽。讀者可以根據(jù)自己的需要在服務網(wǎng)關上定義一些與業(yè)務無關的通用邏輯實現(xiàn)對請求的過濾和攔截埠戳,比如:簽名校驗、權限校驗蕉扮、請求限流等功能整胃。

進階閱讀

為了更好的理解和擴展Spring Cloud Zuul,我們可以閱讀下面這些文章喳钟,有助于深入的了解其內(nèi)部運行機制屁使,以指導我們合理的編寫過濾器邏輯:

最后編輯于
?著作權歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末在岂,一起剝皮案震驚了整個濱河市,隨后出現(xiàn)的幾起案子蛮寂,更是在濱河造成了極大的恐慌蔽午,老刑警劉巖,帶你破解...
    沈念sama閱讀 216,372評論 6 498
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件酬蹋,死亡現(xiàn)場離奇詭異及老,居然都是意外死亡,警方通過查閱死者的電腦和手機范抓,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 92,368評論 3 392
  • 文/潘曉璐 我一進店門骄恶,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人匕垫,你說我怎么就攤上這事僧鲁。” “怎么了象泵?”我有些...
    開封第一講書人閱讀 162,415評論 0 353
  • 文/不壞的土叔 我叫張陵悔捶,是天一觀的道長。 經(jīng)常有香客問我单芜,道長蜕该,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 58,157評論 1 292
  • 正文 為了忘掉前任洲鸠,我火速辦了婚禮堂淡,結果婚禮上,老公的妹妹穿的比我還像新娘扒腕。我一直安慰自己绢淀,他們只是感情好,可當我...
    茶點故事閱讀 67,171評論 6 388
  • 文/花漫 我一把揭開白布瘾腰。 她就那樣靜靜地躺著皆的,像睡著了一般。 火紅的嫁衣襯著肌膚如雪蹋盆。 梳的紋絲不亂的頭發(fā)上费薄,一...
    開封第一講書人閱讀 51,125評論 1 297
  • 那天,我揣著相機與錄音栖雾,去河邊找鬼楞抡。 笑死,一個胖子當著我的面吹牛析藕,可吹牛的內(nèi)容都是我干的召廷。 我是一名探鬼主播,決...
    沈念sama閱讀 40,028評論 3 417
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼竞慢!你這毒婦竟也來了先紫?” 一聲冷哼從身側響起,我...
    開封第一講書人閱讀 38,887評論 0 274
  • 序言:老撾萬榮一對情侶失蹤筹煮,失蹤者是張志新(化名)和其女友劉穎遮精,沒想到半個月后,有當?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體寺谤,經(jīng)...
    沈念sama閱讀 45,310評論 1 310
  • 正文 獨居荒郊野嶺守林人離奇死亡仑鸥,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 37,533評論 2 332
  • 正文 我和宋清朗相戀三年,在試婚紗的時候發(fā)現(xiàn)自己被綠了变屁。 大學時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片眼俊。...
    茶點故事閱讀 39,690評論 1 348
  • 序言:一個原本活蹦亂跳的男人離奇死亡,死狀恐怖粟关,靈堂內(nèi)的尸體忽然破棺而出疮胖,到底是詐尸還是另有隱情,我是刑警寧澤闷板,帶...
    沈念sama閱讀 35,411評論 5 343
  • 正文 年R本政府宣布澎灸,位于F島的核電站,受9級特大地震影響遮晚,放射性物質(zhì)發(fā)生泄漏性昭。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點故事閱讀 41,004評論 3 325
  • 文/蒙蒙 一县遣、第九天 我趴在偏房一處隱蔽的房頂上張望糜颠。 院中可真熱鬧,春花似錦萧求、人聲如沸其兴。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,659評論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽元旬。三九已至,卻和暖如春守问,著一層夾襖步出監(jiān)牢的瞬間匀归,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 32,812評論 1 268
  • 我被黑心中介騙來泰國打工酪碘, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留朋譬,地道東北人。 一個月前我還...
    沈念sama閱讀 47,693評論 2 368
  • 正文 我出身青樓兴垦,卻偏偏與公主長得像,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個殘疾皇子探越,可洞房花燭夜當晚...
    茶點故事閱讀 44,577評論 2 353

推薦閱讀更多精彩內(nèi)容