登錄工程:現(xiàn)代 Web 應(yīng)用中的身份驗(yàn)證技術(shù)

在“登錄工程”的前兩篇文章分別介紹了傳統(tǒng) Web 應(yīng)用中的身份驗(yàn)證技術(shù),以及現(xiàn)代 Web 應(yīng)用中的典型身份驗(yàn)證需求,接下來(lái)是時(shí)候介紹適應(yīng)于現(xiàn)代 Web 應(yīng)用中的身份驗(yàn)證實(shí)踐了鹉究。

安全議題不容忽視

登錄系統(tǒng)

首先妹田,我們?yōu)榈卿涀鲆粋€(gè)簡(jiǎn)要的定義芯勘,以令后續(xù)的講述更準(zhǔn)確。之前的兩篇文章有意無(wú)意地混淆了“登錄”與“身份驗(yàn)證”的說(shuō)法淳地,因?yàn)樵诒酒埃瑢?duì)于不少“傳統(tǒng) Web 應(yīng)用”來(lái)說(shuō)帅容,通常完成了對(duì)身份的識(shí)別也就完成了整個(gè)登錄的過(guò)程颇象,很少出現(xiàn)像企業(yè)應(yīng)用環(huán)境中那樣復(fù)雜的情景和需求。但從之前的文章中我們看到并徘,現(xiàn)代 Web 應(yīng)用對(duì)身份驗(yàn)證相關(guān)的需求已經(jīng)向復(fù)雜化發(fā)展了遣钳。

我們有必要重新認(rèn)識(shí)一下登錄系統(tǒng)。登錄指的是從識(shí)別用戶身份麦乞,到允許用戶訪問(wèn)其權(quán)限相應(yīng)的資源的過(guò)程耍贾。舉個(gè)例子,在網(wǎng)上買好了票之后去影院觀影的過(guò)程就是一個(gè)典型的登錄過(guò)程:我們先去取票機(jī)路幸,輸入取票驗(yàn)證碼取票荐开;接著拿到票去影廳完檢票進(jìn)入觀影。取票的過(guò)程即身份驗(yàn)證简肴,它能夠證明我們擁有這張票晃听;而后面檢票的過(guò)程,則是授權(quán)我們?cè)L問(wèn)的過(guò)程砰识。之所以要分成這兩個(gè)過(guò)程能扒,最直接的原因還是業(yè)務(wù)形態(tài)本身的復(fù)雜性所決定的——如果觀景過(guò)程是免費(fèi)匿名的,也就免去了這些過(guò)程辫狼。

在登錄的過(guò)程中初斑,“鑒權(quán)”與“授權(quán)”是兩個(gè)最關(guān)鍵的過(guò)程。接下來(lái)要介紹的一些技術(shù)和實(shí)踐膨处,也包含在這兩個(gè)方面中见秤。雖然現(xiàn)代 Web 應(yīng)用的登錄需求比較復(fù)雜,但只要處理好了鑒權(quán)和授權(quán)兩個(gè)方面真椿,也就容易解決其余各個(gè)方面鹃答。在現(xiàn)代 Web 應(yīng)用的登錄工程實(shí)踐中,需要結(jié)合傳統(tǒng) Web 應(yīng)用的典型實(shí)踐突硝,以及一些新的思路测摔,才能既解決好登錄需求,又能符合 Web 的輕量級(jí)架構(gòu)思路。


解析常見的登錄場(chǎng)景

在簡(jiǎn)單的 Web 系統(tǒng)中锋八,典型的鑒權(quán)也就是要求用戶輸入并比對(duì)用戶名和密碼的過(guò)程浙于;而授權(quán)則是確保會(huì)話 Cookie 存在。而在稍微復(fù)雜的 Web 系統(tǒng)中挟纱,則需要考慮多種鑒權(quán)方式路媚,以及多種授權(quán)場(chǎng)景。在上一篇文章中所述的“多種登錄方式”和“雙因子鑒權(quán)”就是多種鑒權(quán)方式的例子樊销。有經(jīng)驗(yàn)的人經(jīng)常調(diào)侃說(shuō)整慎,只要理解了鑒權(quán)與授權(quán),就能清晰地理解登錄系統(tǒng)了围苫。不光如此裤园,這也是設(shè)計(jì)安全的登錄系統(tǒng)的基礎(chǔ)所在。

下表是各種常見登錄過(guò)程中的場(chǎng)景與“鑒權(quán)”和“授權(quán)”的對(duì)應(yīng)關(guān)系:

多種場(chǎng)景的登錄

從表中可以看出剂府,鑒權(quán)的形式豐富多彩拧揽,有傳統(tǒng)的用戶名密碼對(duì)、客戶端證書腺占,有人們?cè)絹?lái)越熟悉的第三方登錄淤袜、手機(jī)驗(yàn)證,以及新興的掃碼和指紋等方式衰伯,它們都能用于對(duì)用戶的身份進(jìn)行識(shí)別铡羡。在成功識(shí)別了用戶之后,在用戶訪問(wèn)資源或執(zhí)行操作之前意鲸,我們還需要對(duì)用戶的操作進(jìn)行授權(quán)烦周。

在一些特別簡(jiǎn)單的情形中——用戶一經(jīng)識(shí)別,就可以無(wú)限制地訪問(wèn)資源怎顾、執(zhí)行所有操作——系統(tǒng)直接對(duì)所有“已登錄的人”放行读慎。比如高速公路收費(fèi)站,只要車輛有合法的號(hào)牌即可放行槐雾,不需要給駕駛員發(fā)一張用于指示“允許行駛的方向或時(shí)間”的票據(jù)夭委。除了這類特別簡(jiǎn)單的情形之外,授權(quán)更多時(shí)候是比較復(fù)雜的工作募强。在單一的傳統(tǒng) Web 應(yīng)用中株灸,授權(quán)的過(guò)程通常由會(huì)話 Cookie 來(lái)完成——只要服務(wù)器發(fā)現(xiàn)瀏覽器攜帶了對(duì)應(yīng)的 Cookie,即允許用戶訪問(wèn)資源钻注、執(zhí)行操作蚂且。而在瀏覽器之外,例如在 Web API 調(diào)用幅恋、移動(dòng)應(yīng)用和富 Web 應(yīng)用等場(chǎng)景中,要提供安全又不失靈活的授權(quán)方式泵肄,就需要借助令牌技術(shù)捆交。


令牌

令牌是一個(gè)在各種介紹登錄技術(shù)的文章中常被提及的概念淑翼,而且是在現(xiàn)代 Web 應(yīng)用系統(tǒng)中非常關(guān)鍵的技術(shù)。令牌是一個(gè)非常簡(jiǎn)單的概念品追,它指的是在用戶通過(guò)身份驗(yàn)證之后玄括,為用戶分配的一個(gè)臨時(shí)憑證。在系統(tǒng)內(nèi)部肉瓦,各個(gè)子系統(tǒng)只需要以統(tǒng)一的方式正確識(shí)別和處理這個(gè)憑證即可完成對(duì)用戶的訪問(wèn)和操作進(jìn)行授權(quán)遭京。在上文所提到的例子中,電影票就是一個(gè)典型的令牌泞莉。影廳門口的工作人員只需要確認(rèn)來(lái)客手持印有對(duì)應(yīng)場(chǎng)次的電影票即視為合法訪問(wèn)哪雕,而不需要理會(huì)客戶是從何種渠道取得了電影票(比如自行購(gòu)買、朋友贈(zèng)予等)鲫趁,電影票在本場(chǎng)次范圍內(nèi)可以持續(xù)使用(比如可以中場(chǎng)出去休息等)斯嚎、過(guò)期作廢。通過(guò)電影票這樣一個(gè)簡(jiǎn)單的令牌機(jī)制挨厚,電影票的出售渠道可以豐富多樣堡僻,檢票人員的工作卻仍然簡(jiǎn)單輕松。

從這個(gè)例子也可以看出令牌并非什么神奇的機(jī)制疫剃,只是一種很常見的做法钉疫。還記得第一篇文章中所述的“自包含的 Cookie”嗎?那實(shí)際上就是一個(gè)令牌而已巢价,而且在令牌中寫有有關(guān)自己有效性的內(nèi)容——正如一個(gè)電影票上會(huì)寫明場(chǎng)次與影廳編號(hào)一樣陌选。可見蹄溉,在 Web 安全系統(tǒng)中引入令牌的做法咨油,有著與傳統(tǒng)場(chǎng)合一樣的妙用。在安全系統(tǒng)中柒爵,令牌經(jīng)常用于包含安全上下文信息役电,例如被識(shí)別的用戶信息,令牌的頒發(fā)來(lái)源棉胀,令牌本身的有效期等法瑟。另外,在必要時(shí)可以由系統(tǒng)廢止令牌唁奢,在它下次被使用用于訪問(wèn)霎挟、操作時(shí),用戶被禁止麻掸。

由于令牌有這些特殊的妙用酥夭,因此安全行業(yè)對(duì)令牌的標(biāo)準(zhǔn)的制定工作一直沒(méi)有停止過(guò)。在現(xiàn)代化 Web 系統(tǒng)的演進(jìn)過(guò)程中,流行的方式是傾向于選用基于 Web 技術(shù)的“簡(jiǎn)單”的技術(shù)來(lái)代替相對(duì)復(fù)雜熬北、重量級(jí)的技術(shù)疙描。典型地,比如使用 JSON-RPC 或 REST 接口代替了 SOAP 格式的服務(wù)調(diào)用讶隐,用微服務(wù)架構(gòu)代替了 SOA 架構(gòu)等等起胰。而適用于 Web 技術(shù)的令牌標(biāo)準(zhǔn)就是 Json Web Token(JWT),它規(guī)范了一種基于 JSON 的令牌的簡(jiǎn)單格式巫延,可用于安全地封裝安全上下文信息效五。


OAuth 2、Open ID Connect

OAuth 是一種開放的授權(quán)模型炉峰,它規(guī)定了一種供資源擁有方與消費(fèi)方之間簡(jiǎn)單又直觀的交互方法畏妖,即從消費(fèi)方向資源擁有方發(fā)起使用 AccessToken(訪問(wèn)令牌)簽名的 HTTP 請(qǐng)求。這種方式讓消費(fèi)方應(yīng)用在無(wú)需(也無(wú)法)獲得用戶憑據(jù)的情況下讲冠,只要用戶完成鑒權(quán)過(guò)程并同意消費(fèi)方以自己的身份調(diào)用數(shù)據(jù)和操作瓜客,消費(fèi)方就可以獲得能夠完成功能的訪問(wèn)令牌。OAuth 簡(jiǎn)單的流程和自由的編程模型讓它很好地滿足了開放平臺(tái)場(chǎng)景中授權(quán)第三方應(yīng)用使用用戶數(shù)據(jù)的需求竿开。不少互聯(lián)網(wǎng)公司建設(shè)開放平臺(tái)谱仪,將它們的用戶在其平臺(tái)上的數(shù)據(jù)以 API 的形式開放給第三方應(yīng)用來(lái)使用,從而可以讓用戶享受更豐富的服務(wù)否彩。

OAuth 在各個(gè)開放平臺(tái)的成功使用疯攒,令更多開發(fā)者了解到它,并被它簡(jiǎn)單明確的流程所吸引列荔。此外敬尺,OAuth 協(xié)議規(guī)定的是授權(quán)模型,并不規(guī)定訪問(wèn)令牌的數(shù)據(jù)格式贴浙,也不限制在整個(gè)登錄過(guò)程中需要使用的鑒權(quán)方法砂吞。人們很快發(fā)現(xiàn),只要對(duì) OAuth 進(jìn)行合適的利用即可將其用于各種自有系統(tǒng)中的場(chǎng)景崎溃。例如蜻直,將 Web 服務(wù)視作資源擁有方,而將富 Web 應(yīng)用或者移動(dòng)應(yīng)用視作消費(fèi)方應(yīng)用袁串,就與開放平臺(tái)的場(chǎng)景完全吻合概而。

另一個(gè)大量實(shí)踐的場(chǎng)景是基于 OAuth 的單點(diǎn)登錄。OAuth 并沒(méi)有對(duì)鑒權(quán)的部分做規(guī)定囱修,也不要求在握手交互過(guò)程中包含用戶的身份信息赎瑰,因此它并不適合作為單點(diǎn)登錄系統(tǒng)來(lái)使用。然而破镰,由于 OAuth 的流程中隱含了鑒權(quán)的步驟餐曼,因而仍然有不少開發(fā)者將這一鑒權(quán)的步驟用作單點(diǎn)登錄系統(tǒng)压储,這也儼然衍生成為一種實(shí)踐模式。更有人將這個(gè)實(shí)踐進(jìn)行了標(biāo)準(zhǔn)化晋辆,它就是 Open ID Connect——基于 OAuth 的身份上下文協(xié)議渠脉,通過(guò)它即可以 JWT 的形式安全地在多個(gè)應(yīng)用中共享用戶身份宇整。接下來(lái)瓶佳,只要讓鑒權(quán)服務(wù)器支持較長(zhǎng)的會(huì)話時(shí)間,就可以利用 OAuth 為多個(gè)業(yè)務(wù)系統(tǒng)提供單點(diǎn)登錄功能了鳞青。

我們還沒(méi)有討論 OAuth 對(duì)鑒權(quán)系統(tǒng)的影響霸饲。實(shí)際上,OAuth 對(duì)鑒權(quán)系統(tǒng)沒(méi)有影響臂拓,在它的框架內(nèi)厚脉,只是假設(shè)已經(jīng)存在了一種可用于識(shí)別用戶的有效機(jī)制,而這種機(jī)制具體是怎么工作的胶惰,OAuth 并不關(guān)心傻工。因此我們既可以使用用戶名密碼(大多數(shù)開放平臺(tái)提供商都是這種方式),也可以使用掃碼登錄來(lái)識(shí)別用戶孵滞,更可以提供諸如“記住密碼”中捆,或者雙因子驗(yàn)證等其他功能。


匯總

上面羅列了大量術(shù)語(yǔ)和解釋坊饶,那么具體到一個(gè)典型的 Web 系統(tǒng)中泄伪,又應(yīng)該如何對(duì)安全系統(tǒng)進(jìn)行設(shè)計(jì)呢?綜合這些技術(shù)匿级,從端到云蟋滴,從 Web 門戶到內(nèi)部服務(wù),本文給出如下架構(gòu)方案建議:

推薦為整個(gè)應(yīng)用的所有系統(tǒng)痘绎、子系統(tǒng)都部署全程的 HTTPS津函,如果出于性能和成本考慮做不到,那么至少要保證在用戶或設(shè)備直接訪問(wèn)的 Web 應(yīng)用中全程使用 HTTPS孤页。

用不同的系統(tǒng)分別用作身份和登錄尔苦,以及業(yè)務(wù)服務(wù)。當(dāng)用戶登錄成功之后散庶,使用 OpenID Connect 向業(yè)務(wù)系統(tǒng)頒發(fā) JWT 格式的訪問(wèn)令牌和身份信息蕉堰。如果需要,登錄系統(tǒng)可以提供多種登錄方式悲龟,或者雙因子登錄等增強(qiáng)功能屋讶。作為安全令牌服務(wù)(STS),它還負(fù)責(zé)頒發(fā)须教、刷新皿渗、驗(yàn)證和取消令牌的操作斩芭。在身份驗(yàn)證的整個(gè)流程的每一個(gè)步驟,都使用 OAuth 及 JWT 中內(nèi)置的機(jī)制驗(yàn)證數(shù)據(jù)的來(lái)源方是可信的:登錄系統(tǒng)要申請(qǐng)登錄請(qǐng)求來(lái)自受認(rèn)可的業(yè)務(wù)應(yīng)用乐疆,而業(yè)務(wù)在獲得令牌之后也需要驗(yàn)證令牌的有效性划乖。

在 Web 頁(yè)面應(yīng)用中,應(yīng)該申請(qǐng)時(shí)效較短的令牌挤土。將獲取到的令牌向客戶端頁(yè)面中以 httponly 的方式寫入會(huì)話 Cookie琴庵,以用于后續(xù)請(qǐng)求的授權(quán);在后緒請(qǐng)求到達(dá)時(shí)仰美,驗(yàn)證請(qǐng)求中所攜帶的令牌迷殿,并延長(zhǎng)其時(shí)效】г樱基于 JWT 自包含的特性庆寺,輔以完備的簽名認(rèn)證,Web 應(yīng)用無(wú)需額外地維護(hù)會(huì)話狀態(tài)诉字。

在富客戶端 Web 應(yīng)用(單頁(yè)應(yīng)用)懦尝,或者移動(dòng)端、客戶端應(yīng)用中壤圃,可按照應(yīng)用業(yè)務(wù)形態(tài)申請(qǐng)時(shí)效較長(zhǎng)的令牌陵霉,或者用較短時(shí)效的令牌、配合專用的刷新令牌使用埃唯。

在 Web 應(yīng)用的子系統(tǒng)之間撩匕,調(diào)用其他子服務(wù)時(shí),可靈活使用“應(yīng)用程序身份”(如果該服務(wù)完全不直接對(duì)用戶提供調(diào)用)墨叛,或者將用戶傳入的令牌直接傳遞到受調(diào)用的服務(wù)的方式進(jìn)行授權(quán)止毕。各個(gè)業(yè)務(wù)系統(tǒng)可結(jié)合基于角色的訪問(wèn)控制(RBAC)開發(fā)自有專用權(quán)限系統(tǒng)。


作為工程師漠趁,我們不免會(huì)考慮扁凛,既然登錄系統(tǒng)的需求可能如此復(fù)雜,而大家面臨的需求在很多時(shí)候又是如此類似闯传,那么有沒(méi)有什么現(xiàn)成(Out of Box)的解決方案呢谨朝?自然是有的。IdentityServer 是一個(gè)完整的開發(fā)框架甥绿,提供了普通登錄到 OAuth 和 Open ID Connect 的完整實(shí)現(xiàn)字币;Open AM 是一個(gè)開源的單點(diǎn)登錄與訪問(wèn)管理軟件平臺(tái);而 Microsoft Azure AD 和 AWS IAM 則是公有云上的身份服務(wù)共缕。幾乎在各個(gè)層次都有現(xiàn)成的方案可用洗出。使用現(xiàn)成的產(chǎn)品和服務(wù),能夠極大地縮減開發(fā)成本图谷,尤其為創(chuàng)業(yè)團(tuán)隊(duì)快速構(gòu)建產(chǎn)品和靈活變化提供更有力的保障翩活。

本文簡(jiǎn)單解釋了登錄過(guò)程中所涉及的基本原理阱洪,以及現(xiàn)代 Web 應(yīng)用中用于身份驗(yàn)證的幾種實(shí)用技術(shù),希望為您在開發(fā)身份驗(yàn)證系統(tǒng)提供幫助〔ふ颍現(xiàn)代 Web 應(yīng)用的身份驗(yàn)證需求多變冗荸,應(yīng)用本身的結(jié)構(gòu)也比傳統(tǒng)的 Web 應(yīng)用更復(fù)雜,需要架構(gòu)師在明確了登錄系統(tǒng)的基本原理的基礎(chǔ)之上利耍,靈活利用各項(xiàng)技術(shù)的優(yōu)勢(shì)蚌本,恰到好處地解決問(wèn)題。

登錄工程的系列文章到此就全部結(jié)束了堂竟,歡迎就文章內(nèi)容提供反饋魂毁。

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末玻佩,一起剝皮案震驚了整個(gè)濱河市出嘹,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌咬崔,老刑警劉巖税稼,帶你破解...
    沈念sama閱讀 222,627評(píng)論 6 517
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場(chǎng)離奇詭異垮斯,居然都是意外死亡郎仆,警方通過(guò)查閱死者的電腦和手機(jī),發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 95,180評(píng)論 3 399
  • 文/潘曉璐 我一進(jìn)店門兜蠕,熙熙樓的掌柜王于貴愁眉苦臉地迎上來(lái)扰肌,“玉大人,你說(shuō)我怎么就攤上這事熊杨∈镄瘢” “怎么了?”我有些...
    開封第一講書人閱讀 169,346評(píng)論 0 362
  • 文/不壞的土叔 我叫張陵晶府,是天一觀的道長(zhǎng)桂躏。 經(jīng)常有香客問(wèn)我,道長(zhǎng)川陆,這世上最難降的妖魔是什么剂习? 我笑而不...
    開封第一講書人閱讀 60,097評(píng)論 1 300
  • 正文 為了忘掉前任,我火速辦了婚禮较沪,結(jié)果婚禮上鳞绕,老公的妹妹穿的比我還像新娘。我一直安慰自己尸曼,他們只是感情好们何,可當(dāng)我...
    茶點(diǎn)故事閱讀 69,100評(píng)論 6 398
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著骡苞,像睡著了一般垂蜗。 火紅的嫁衣襯著肌膚如雪楷扬。 梳的紋絲不亂的頭發(fā)上,一...
    開封第一講書人閱讀 52,696評(píng)論 1 312
  • 那天贴见,我揣著相機(jī)與錄音烘苹,去河邊找鬼。 笑死片部,一個(gè)胖子當(dāng)著我的面吹牛镣衡,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播档悠,決...
    沈念sama閱讀 41,165評(píng)論 3 422
  • 文/蒼蘭香墨 我猛地睜開眼廊鸥,長(zhǎng)吁一口氣:“原來(lái)是場(chǎng)噩夢(mèng)啊……” “哼!你這毒婦竟也來(lái)了辖所?” 一聲冷哼從身側(cè)響起惰说,我...
    開封第一講書人閱讀 40,108評(píng)論 0 277
  • 序言:老撾萬(wàn)榮一對(duì)情侶失蹤,失蹤者是張志新(化名)和其女友劉穎缘回,沒(méi)想到半個(gè)月后吆视,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 46,646評(píng)論 1 319
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡酥宴,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 38,709評(píng)論 3 342
  • 正文 我和宋清朗相戀三年啦吧,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片拙寡。...
    茶點(diǎn)故事閱讀 40,861評(píng)論 1 353
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡授滓,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出肆糕,到底是詐尸還是另有隱情般堆,我是刑警寧澤,帶...
    沈念sama閱讀 36,527評(píng)論 5 351
  • 正文 年R本政府宣布擎宝,位于F島的核電站郁妈,受9級(jí)特大地震影響,放射性物質(zhì)發(fā)生泄漏绍申。R本人自食惡果不足惜噩咪,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 42,196評(píng)論 3 336
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望极阅。 院中可真熱鬧胃碾,春花似錦、人聲如沸筋搏。這莊子的主人今日做“春日...
    開封第一講書人閱讀 32,698評(píng)論 0 25
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽(yáng)奔脐。三九已至俄周,卻和暖如春吁讨,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 33,804評(píng)論 1 274
  • 我被黑心中介騙來(lái)泰國(guó)打工, 沒(méi)想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留椰憋,地道東北人。 一個(gè)月前我還...
    沈念sama閱讀 49,287評(píng)論 3 379
  • 正文 我出身青樓翎朱,卻偏偏與公主長(zhǎng)得像,于是被迫代替她去往敵國(guó)和親尺铣。 傳聞我的和親對(duì)象是個(gè)殘疾皇子拴曲,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 45,860評(píng)論 2 361

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