使用 OAuth 2 和 JWT 為微服務(wù)提供安全保障

Part 1 - 理論相關(guān)

作者 freewolf

關(guān)鍵詞

微服務(wù)Spring Cloud瞒大、OAuth 2.0系洛、JWT俊性、Spring SecuritySSO描扯、UAA

寫在前面

作為從業(yè)了十多年的IT行業(yè)和程序的老司機(jī)定页,今天如果你說你不懂微服務(wù),都不好意思說自己的做軟件的绽诚。SOA喊了多年典徊,無人不知,但又有多少系統(tǒng)開發(fā)真正的SOA了呢恩够?但是好像一夜之間所有人都投入了微服務(wù)的懷抱卒落。

作為目前最主流的“微服務(wù)框架”,Spring Cloud發(fā)展速度很快蜂桶,成為了最全面的微服務(wù)解決方案儡毕。不管什么軟件體系,什么框架扑媚,安全永遠(yuǎn)是不可能繞開的話題妥曲,我也把它作為我最近一段時間研究微服務(wù)的開篇。

老話題钦购!“如何才能在微服務(wù)體系中保證安全檐盟?”,為了達(dá)成目標(biāo)押桃,這里采用一個簡單而可行方式來保護(hù)Spring Cloud中服務(wù)的安全葵萎,也就是建立統(tǒng)一的用戶授權(quán)中心。

這里補(bǔ)充說一下什么是Authentication(認(rèn)證)Authorization(鑒權(quán)),其實很簡單羡忘,認(rèn)證關(guān)心你是誰谎痢,鑒權(quán)關(guān)心你能干什么。舉個大家一致都再說的例子卷雕,如果你去機(jī)場乘機(jī)节猿,你持有的護(hù)照代表你的身份,這是認(rèn)證漫雕,你的機(jī)票就是你的權(quán)限滨嘱,你能干什么。

學(xué)習(xí)微服務(wù)并不是一個簡單的探索過程浸间,這不得學(xué)習(xí)很多新的知識太雨,其實不管是按照DDD(Domain Driven Design)領(lǐng)域驅(qū)動設(shè)計中領(lǐng)域模型的方式,還是將微服務(wù)拆分成更小的粒度魁蒜。都會遇到很多新的問題和以前一直都沒解決很好的問題囊扳。隨著不斷的思考,隨著熟悉Facebook/GitHub/AWS這些機(jī)構(gòu)是如何保護(hù)內(nèi)部資源兜看,答案也逐漸浮出水面锥咸。

為了高效的實現(xiàn)這個目標(biāo),這里采用OAuth 2JWT(JSON Web Tokens)技術(shù)作為解決方案细移,

為什么使用OAuth 2

盡管微服務(wù)在現(xiàn)代軟件開發(fā)中還算一個新鮮事物搏予,但是OAuth 2已經(jīng)是一個廣泛使用的授權(quán)技術(shù),它讓W(xué)eb開發(fā)者在自己提供服務(wù)中葫哗,用一種安全的方式直訪問Google/Facebook/GitHub平臺用戶信息。但在我開始闡述細(xì)節(jié)之前球涛,我將揭開聚焦到本文真正的主題:云安全

那么在云服務(wù)中對用戶訪問資源的控制劣针,我們一般都怎么做呢?然我舉一些大家似乎都用過的但又不是很完美的例子亿扁。

我們可以設(shè)置邊界服務(wù)器或者帶認(rèn)證功能的反向代理服務(wù)器捺典,假設(shè)所有訪問請求都發(fā)給它。通過認(rèn)證后从祝,轉(zhuǎn)發(fā)給內(nèi)部相應(yīng)的服務(wù)器襟己。一般在Spring MVC Security開發(fā)中幾乎都會這樣做的。但這并不安全牍陌,最重要的是擎浴,一旦是有人從內(nèi)部攻擊,你的數(shù)據(jù)毫無安全性毒涧。

其他方式:我們?yōu)樗蟹?wù)建立統(tǒng)一的權(quán)限數(shù)據(jù)庫贮预,并在每次請求前對用戶進(jìn)行鑒權(quán),聽起來某些方面的確有點(diǎn)愚蠢,但實際上這確實是一個可行的安全方案仿吞。

更好的方式: 用戶通過授權(quán)服務(wù)來實現(xiàn)鑒權(quán)滑频,把用戶訪問Session映射成一個Token。所有遠(yuǎn)程訪問資源服務(wù)器相關(guān)的API必須提供Token唤冈。然后資源服務(wù)器訪問授權(quán)服務(wù)來識別Token峡迷,得知Token屬于哪個用戶,并了解通過這個Token可以訪問什么資源你虹。

這聽起來是個不錯的方案绘搞,對不?但是如何保證Token的安全傳輸售葡?如何區(qū)分是用戶訪問還是其他服務(wù)訪問看杭?這肯定是我們關(guān)心的問題。

所以上述種種問題讓我們選擇使用OAuth 2挟伙,其實訪問Facebook/Google的敏感數(shù)據(jù)和訪問我們自己后端受保護(hù)數(shù)據(jù)沒什么區(qū)別楼雹,并且他們已經(jīng)使用這樣的解決方案很多年,我們只要遵循這些方法就好了尖阔。

OAuth 2是如何工作的

如果你了解OAuth 2相關(guān)的原理贮缅,那么在部署OAuth 2是非常容易的。
讓我們描述下這樣一個場景介却,“某App希望獲得TomFacebook上相關(guān)的數(shù)據(jù)”

OAuth 2 在整個流程中有四種角色:

  • 資源擁有者(Resource Owner) - 這里是Tom
  • 資源服務(wù)器(Resource Server) - 這里是Facebook
  • 授權(quán)服務(wù)器(Authorization Server) - 這里當(dāng)然還是Facebook谴供,因為Facebook有相關(guān)數(shù)據(jù)
  • 客戶端(Client) - 這里是某App

當(dāng)Tom試圖登錄Facebook某App將他重定向到Facebook的授權(quán)服務(wù)器齿坷,當(dāng)Tom登錄成功桂肌,并且許可自己的Email和個人信息被某App獲取。這兩個資源被定義成一個Scope(權(quán)限范圍)永淌,一旦準(zhǔn)許崎场,某App的開發(fā)者就可以申請訪問權(quán)限范圍中定義的這兩個資源。

+--------+                               +---------------+
|        |--(A)- Authorization Request ->|   Resource    |
|        |                               |     Owner     |
|        |<-(B)-- Authorization Grant ---|               |
|        |                               +---------------+
|        |
|        |                               +---------------+
|        |--(C)-- Authorization Grant -->| Authorization |
| Client |                               |     Server    |
|        |<-(D)----- Access Token -------|               |
|        |                               +---------------+
|        |
|        |                               +---------------+
|        |--(E)----- Access Token ------>|    Resource   |
|        |                               |     Server    |
|        |<-(F)--- Protected Resource ---|               |
+--------+                               +---------------+

Tom允許了權(quán)限請求遂蛀,再次通過重定向返回某App谭跨,重定向返回時攜帶了一個Access Token(訪問令牌),接下來某App就可以通過這個Access TokenFacebook直接獲取相關(guān)的授權(quán)資源(也就是Email和個人信息)李滴,而無需重新做Tom相關(guān)的鑒權(quán)螃宙。而且每當(dāng)Tom登錄了某App,都可以通過之前獲得的Access Token所坯,直接獲取相關(guān)授權(quán)資源谆扎。

到目前為止,我們?nèi)绾沃苯訉⒁陨蟽?nèi)容用于實際的例子中芹助?OAuth 2十分友好燕酷,并容易部署籍凝,所有交互都是關(guān)于客戶端和權(quán)限范圍的。

  • OAuth 2中的客戶端權(quán)限范圍和我們平時的用戶和權(quán)限是否相同苗缩?
  • 我需要將授權(quán)映射到權(quán)限范圍中或?qū)⒂脩粲成涞娇蛻舳酥校?/li>
  • 為什么我需要客戶端饵蒂?

你也許在之前在類似的企業(yè)級開發(fā)案例中嘗試映射過相關(guān)的角色。這會很棘手酱讶!

任何類型的應(yīng)用都提供用戶登錄退盯,登錄結(jié)果是一個Access Token,所有的之后的API調(diào)用都將這個Access Token加入HTTP請求頭中泻肯,被調(diào)用服務(wù)去授權(quán)服務(wù)器驗證Access Token并獲取該Token可訪問的權(quán)限信息渊迁。這樣一來,所有服務(wù)的訪問都會請求另外的服務(wù)來完成鑒權(quán)灶挟。

權(quán)限范圍和角色琉朽,客戶端和用戶

OAuth 2中,你可以定義哪個應(yīng)用(網(wǎng)站稚铣、移動客戶端箱叁、桌面應(yīng)用、其他)可以訪問那些資源惕医。這里只有一個尺寸耕漱,來自哪里的哪個用戶可以訪問那些數(shù)據(jù),當(dāng)然也是哪個應(yīng)用或者服務(wù)可以訪問那些資源抬伺。換一種說法螟够,權(quán)限范圍就是控制那些端點(diǎn)對客戶端可見,或者用戶根據(jù)他的權(quán)限來獲取相關(guān)的數(shù)據(jù)峡钓。

在一個在線商店中妓笙,前端可以看做一個客戶端,可以訪問商品能岩、訂單和客戶信息寞宫,但后端可以關(guān)于物流和合同等,另一方面捧灰,用戶可以訪問一個服務(wù)但并不是全部的數(shù)據(jù)淆九,這可以是因為用戶正在使用Web應(yīng)用统锤,當(dāng)他不能的時候毛俏,其他用戶卻可以。服務(wù)之間的訪問時我們要討論的另一個維度饲窿。如果你熟悉數(shù)學(xué)煌寇,我可以說在OAuth 2中,客戶端-權(quán)限范圍關(guān)系是線性獨(dú)立于用戶-權(quán)限關(guān)系逾雄。

為什么是JWT

OAuth 2并不關(guān)心去哪找Access Token和把它存在什么地方的阀溶,生成隨機(jī)字符串并保存Token相關(guān)的數(shù)據(jù)到這些字符串中保存好腻脏。通過一個令牌端點(diǎn),其他服務(wù)可能會關(guān)心這個Token是否有效银锻,它可以通過哪些權(quán)限永品。這就是用戶信息URL方法,授權(quán)服務(wù)器為了獲取用戶信息轉(zhuǎn)換為資源服務(wù)器击纬。

當(dāng)我們談及微服務(wù)時鼎姐,我們需要找一個Token存儲的方式,來保證授權(quán)服務(wù)器可以被水平擴(kuò)展更振,盡管這是一個很復(fù)雜的任務(wù)炕桨。所有訪問微服務(wù)資源的請求都在Http Header中攜帶Token,被訪問的服務(wù)接下來再去請求授權(quán)服務(wù)器驗證Token的有效性肯腕,目前這種方式献宫,我們需要兩次或者更多次的請求,但這是為了安全性也沒什么其他辦法实撒。但擴(kuò)展Token存儲會很大影響我們系統(tǒng)的可擴(kuò)展性姊途,這是我們引入JWT(讀jot)的原因。

+-----------+                                     +-------------+
|           |       1-Request Authorization       |             |
|           |------------------------------------>|             |
|           |     grant_type&username&password    |             |--+
|           |                                     |Authorization|  | 2-Gen
|  Client   |                                     |Service      |  |   JWT
|           |       3-Response Authorization      |             |<-+
|           |<------------------------------------| Private Key |
|           |    access_token / refresh_token     |             |
|           |    token_type / expire_in / jti     |             |
+-----------+                                     +-------------+

簡短來說奈惑,響應(yīng)一個用戶請求時吭净,將用戶信息和授權(quán)范圍序列化后放入一個JSON字符串,然后使用Base64進(jìn)行編碼肴甸,最終在授權(quán)服務(wù)器用私鑰對這個字符串進(jìn)行簽名寂殉,得到一個JSON Web Token,我們可以像使用Access Token一樣的直接使用它原在,假設(shè)其他所有的資源服務(wù)器都將持有一個RSA公鑰友扰。當(dāng)資源服務(wù)器接收到這個在Http Header中存有Token的請求,資源服務(wù)器就可以拿到這個Token庶柿,并驗證它是否使用正確的私鑰簽名(是否經(jīng)過授權(quán)服務(wù)器簽名村怪,也就是驗簽)。驗簽通過浮庐,反序列化后就拿到OAuth 2的驗證信息甚负。

驗證服務(wù)器返回的信息可以是以下內(nèi)容:

  • access_token - 訪問令牌,用于資源訪問
  • refresh_token - 當(dāng)訪問令牌失效审残,使用這個令牌重新獲取訪問令牌
  • token_type - 令牌類型梭域,這里是Bearer也就是基本HTTP認(rèn)證
  • expire_in - 過期時間
  • jti - JWT ID

由于Access TokenBase64編碼,反編碼后就是下面的格式搅轿,標(biāo)準(zhǔn)的JWT格式病涨。也就是HeaderPayload璧坟、Signature三部分既穆。

{ 
  "alg":"RS256",
  "typ":"JWT"
}
{
  "exp": 1492873315,
  "user_name": "reader",
  "authorities": [
    "AURH_READ"
  ],
  "jti": "8f2d40eb-0d75-44df-a8cc-8c37320e3548",
  "client_id": "web_app",
  "scope": [
    "FOO"
  ]
}
&:l?s)???-[??+
F"2"K???8??:u9??? 9Q?32Z??$ec{3mxJ?h??0D?F庖?[?!?N)?knVV?V|夻??E??}??f9>'<蕱?B?е?o?v虀D?8C?4???K}Em?? YVcaqIW&*u?u??b!?*?\?-{??X??WTq

使用JWT可以簡單的傳輸Token赎懦,用RSA簽名保證Token很難被偽造。Access Token字符串中包含用戶信息和權(quán)限范圍幻工,我們所需的全部信息都有了励两,所以不需要維護(hù)Token存儲,資源服務(wù)器也不必要求Token檢查囊颅。

+-----------+                                    +-----------+
|           |       1-Request Resource           |           |
|           |----------------------------------->|           |
|           | Authorization: bearer Access Token |           |--+
|           |                                    | Resource  |  | 2-Verify
|  Client   |                                    | Service   |  |  Token
|           |       3-Response Resource          |           |<-+
|           |<-----------------------------------| Public Key|
|           |                                    |           |
+-----------+                                    +-----------+

所以伐蒋,在微服務(wù)中使用OAuth 2,不會影響到整體架構(gòu)的可擴(kuò)展性迁酸。淡然這里還有一些問題沒有涉及先鱼,例如Access Token過期后,使用Refresh Token到認(rèn)證服務(wù)器重新獲取Access Token等奸鬓,后面會有具體的例子來展開討論這些問題焙畔。

如果您感興趣,后面還會有實現(xiàn)部分串远,敬請期待~

由于 http://asciiflow.com/ 流程圖使用中文就無法對齊了宏多,本文中流程圖都是英文了~

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個濱河市澡罚,隨后出現(xiàn)的幾起案子伸但,更是在濱河造成了極大的恐慌,老刑警劉巖留搔,帶你破解...
    沈念sama閱讀 217,277評論 6 503
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件更胖,死亡現(xiàn)場離奇詭異,居然都是意外死亡隔显,警方通過查閱死者的電腦和手機(jī)却妨,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 92,689評論 3 393
  • 文/潘曉璐 我一進(jìn)店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來括眠,“玉大人彪标,你說我怎么就攤上這事≈啦颍” “怎么了捞烟?”我有些...
    開封第一講書人閱讀 163,624評論 0 353
  • 文/不壞的土叔 我叫張陵,是天一觀的道長当船。 經(jīng)常有香客問我题画,道長,這世上最難降的妖魔是什么生年? 我笑而不...
    開封第一講書人閱讀 58,356評論 1 293
  • 正文 為了忘掉前任婴程,我火速辦了婚禮廓奕,結(jié)果婚禮上抱婉,老公的妹妹穿的比我還像新娘档叔。我一直安慰自己,他們只是感情好蒸绩,可當(dāng)我...
    茶點(diǎn)故事閱讀 67,402評論 6 392
  • 文/花漫 我一把揭開白布衙四。 她就那樣靜靜地躺著,像睡著了一般患亿。 火紅的嫁衣襯著肌膚如雪传蹈。 梳的紋絲不亂的頭發(fā)上,一...
    開封第一講書人閱讀 51,292評論 1 301
  • 那天步藕,我揣著相機(jī)與錄音惦界,去河邊找鬼。 笑死咙冗,一個胖子當(dāng)著我的面吹牛沾歪,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播雾消,決...
    沈念sama閱讀 40,135評論 3 418
  • 文/蒼蘭香墨 我猛地睜開眼灾搏,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了立润?” 一聲冷哼從身側(cè)響起狂窑,我...
    開封第一講書人閱讀 38,992評論 0 275
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎桑腮,沒想到半個月后泉哈,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 45,429評論 1 314
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡破讨,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 37,636評論 3 334
  • 正文 我和宋清朗相戀三年旨巷,在試婚紗的時候發(fā)現(xiàn)自己被綠了。 大學(xué)時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片添忘。...
    茶點(diǎn)故事閱讀 39,785評論 1 348
  • 序言:一個原本活蹦亂跳的男人離奇死亡采呐,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出搁骑,到底是詐尸還是另有隱情斧吐,我是刑警寧澤,帶...
    沈念sama閱讀 35,492評論 5 345
  • 正文 年R本政府宣布仲器,位于F島的核電站煤率,受9級特大地震影響,放射性物質(zhì)發(fā)生泄漏乏冀。R本人自食惡果不足惜蝶糯,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,092評論 3 328
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望辆沦。 院中可真熱鬧昼捍,春花似錦识虚、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,723評論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至乍钻,卻和暖如春肛循,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背银择。 一陣腳步聲響...
    開封第一講書人閱讀 32,858評論 1 269
  • 我被黑心中介騙來泰國打工多糠, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留,地道東北人浩考。 一個月前我還...
    沈念sama閱讀 47,891評論 2 370
  • 正文 我出身青樓熬丧,卻偏偏與公主長得像,于是被迫代替她去往敵國和親怀挠。 傳聞我的和親對象是個殘疾皇子析蝴,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 44,713評論 2 354

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