深入理解OAuth2.0協(xié)議

1. 引言

如果你開(kāi)車去酒店赴宴,你經(jīng)常會(huì)苦于找不到停車位而耽誤很多時(shí)間。是否有好辦法可以避免這個(gè)問(wèn)題呢?有的,聽(tīng)說(shuō)有一些豪車的車主就不擔(dān)心這個(gè)問(wèn)題。豪車一般配備兩種鑰匙:主鑰匙和泊車鑰匙。當(dāng)你到酒店后,只需要將泊車鑰匙交給服務(wù)生,停車的事情就由服務(wù)生去處理。與主鑰匙相比,這種泊車鑰匙的使用功能是受限制的:它只能啟動(dòng)發(fā)動(dòng)機(jī)并讓車行駛一段有限的距離,可以鎖車,但無(wú)法打開(kāi)后備箱,無(wú)法使用車內(nèi)其他設(shè)備。這里就體現(xiàn)了一種簡(jiǎn)單的“開(kāi)放授權(quán)”思想:通過(guò)一把泊車鑰匙,車主便能將汽車的部分使用功能(如啟動(dòng)發(fā)動(dòng)機(jī)、行駛一段有限的距離)授權(quán)給服務(wù)生。

授權(quán)是一個(gè)古老的概念,它是一個(gè)多用戶系統(tǒng)必須支持的功能特性。比如,Alice和Bob都是Google的用戶,那么Alice應(yīng)該可以將自己的照片授權(quán)給Bob訪問(wèn)。但請(qǐng)注意到,這種授權(quán)是一種封閉授權(quán),它只支持系統(tǒng)內(nèi)部用戶之間的相互授權(quán),而不能支持與其他外部系統(tǒng)或用戶之間的授權(quán)。比如說(shuō),Alice想使用“網(wǎng)易印像服務(wù)”將她的部分照片沖印出來(lái),她怎么能做到呢?

肯定有人會(huì)說(shuō),Alice可以將自己的Google用戶名和密碼告訴網(wǎng)易印像服務(wù),事情不就解決了嗎?是的,但只有毫不關(guān)注安全和隱私的同學(xué)才會(huì)出此“絕招”。那么我們就來(lái)想一想,這一“絕招”存在哪些問(wèn)題?(1) 網(wǎng)易印像服務(wù)可能會(huì)緩存Alice的用戶名和密碼,而且可能沒(méi)有加密保護(hù)。它一旦遭到攻擊,Alice就會(huì)躺著中槍。(2) 網(wǎng)易印像服務(wù)可以訪問(wèn)Alice在Google上的所有資源,Alice無(wú)法對(duì)他們進(jìn)行最小的權(quán)限控制,比如只允許訪問(wèn)某一張照片,1小時(shí)內(nèi)訪問(wèn)有效。(3) Alice無(wú)法撤消她的單個(gè)授權(quán),除非Alice更新密碼。

在以Web服務(wù)為核心的云計(jì)算時(shí)代,像用戶Alice的這種授權(quán)需求變得日益迫切與興盛,“開(kāi)放授權(quán)(Open Authorization)”也正因此而生,意在幫助Alice將她的資源授權(quán)給第三方應(yīng)用,支持細(xì)粒度的權(quán)限控制,并且不會(huì)泄漏Alice的密碼或其它認(rèn)證憑據(jù)。

根據(jù)應(yīng)用場(chǎng)景的不同,目前實(shí)現(xiàn)開(kāi)放授權(quán)的方法分為兩種:一種是使用OAuth協(xié)議[1];另一種是使用IAM服務(wù)[2]。OAuth協(xié)議主要適用于針對(duì)個(gè)人用戶對(duì)資源的開(kāi)放授權(quán),比如Google的用戶Alice。OAuth的特點(diǎn)是“現(xiàn)場(chǎng)授權(quán)”或“在線授權(quán)”:客戶端主要通過(guò)瀏覽器去訪問(wèn)資源,授權(quán)時(shí)需要認(rèn)證Alice的資源所有者身份,并且需要Alice現(xiàn)場(chǎng)審批。OAuth一般在SNS服務(wù)中廣泛使用,如微博。IAM服務(wù)則不同,它的特點(diǎn)是“預(yù)先授權(quán)”或“離線授權(quán)”:客戶端主要通過(guò)REST API方式去訪問(wèn)資源,資源所有者可以預(yù)先知道第三方應(yīng)用所需要的資源請(qǐng)求,一次授權(quán)之后,很少會(huì)變更。IAM服務(wù)一般在云計(jì)算服務(wù)中使用,如AWS服務(wù)、阿里云計(jì)算服務(wù)。

本文主要介紹OAuth開(kāi)放授權(quán)。關(guān)于以IAM服務(wù)提供的開(kāi)放授權(quán),我將在另一篇博文中介紹。下面我來(lái)介紹OAuth 2.0協(xié)議、協(xié)議的實(shí)例化描述、安全性分析。

2. OAuth 2.0 協(xié)議

OAuth 2.0 是目前比較流行的做法,它率先被Google, Yahoo, Microsoft, Facebook等使用。之所以標(biāo)注為 2.0,是因?yàn)樽畛跤幸粋€(gè)1.0協(xié)議,但這個(gè)1.0協(xié)議被弄得太復(fù)雜,易用性差,所以沒(méi)有得到普及。2.0是一個(gè)新的設(shè)計(jì),協(xié)議簡(jiǎn)單清晰,但它并不兼容1.0,可以說(shuō)與1.0沒(méi)什么關(guān)系。所以,我就只介紹2.0。

2.1 協(xié)議的參與者

從引言部分的描述我們可以看出,OAuth的參與實(shí)體至少有如下三個(gè):

· RO(resource owner): 資源所有者,對(duì)資源具有授權(quán)能力的人。如上文中的用戶Alice。

· RS(resource server): 資源服務(wù)器,它存儲(chǔ)資源,并處理對(duì)資源的訪問(wèn)請(qǐng)求。如Google資源服務(wù)器,它所保管的資源就是用戶Alice的照片。

· Client: 第三方應(yīng)用,它獲得RO的授權(quán)后便可以去訪問(wèn)RO的資源。如網(wǎng)易印像服務(wù)。

此外,為了支持開(kāi)放授權(quán)功能以及更好地描述開(kāi)放授權(quán)協(xié)議,OAuth引入了第四個(gè)參與實(shí)體:

· AS(authorization server): 授權(quán)服務(wù)器,它認(rèn)證RO的身份,為RO提供授權(quán)審批流程,并最終頒發(fā)授權(quán)令牌(Access Token)。讀者請(qǐng)注意,為了便于協(xié)議的描述,這里只是在邏輯上把AS與RS區(qū)分開(kāi)來(lái);在物理上,AS與RS的功能可以由同一個(gè)服務(wù)器來(lái)提供服務(wù)。

2.2 授權(quán)類型

在開(kāi)放授權(quán)中,第三方應(yīng)用(Client)可能是一個(gè)Web站點(diǎn),也可能是在瀏覽器中運(yùn)行的一段JavaScript代碼,還可能是安裝在本地的一個(gè)應(yīng)用程序。這些第三方應(yīng)用都有各自的安全特性。對(duì)于Web站點(diǎn)來(lái)說(shuō),它與RO瀏覽器是分離的,它可以自己保存協(xié)議中的敏感數(shù)據(jù),這些密鑰可以不暴露給RO;對(duì)于JavaScript代碼和本地安全的應(yīng)用程序來(lái)說(shuō),它本來(lái)就運(yùn)行在RO的瀏覽器中,RO是可以訪問(wèn)到Client在協(xié)議中的敏感數(shù)據(jù)。

OAuth為了支持這些不同類型的第三方應(yīng)用,提出了多種授權(quán)類型,如授權(quán)碼 (Authorization Code Grant)、隱式授權(quán) (Implicit Grant)、RO憑證授權(quán) (Resource Owner Password Credentials Grant)、Client憑證授權(quán) (Client Credentials Grant)。由于本文旨在幫助用戶理解OAuth協(xié)議,所以我將先介紹這些授權(quán)類型的基本思路,然后選擇其中最核心、最難理解、也是最廣泛使用的一種授權(quán)類型——“授權(quán)碼”,進(jìn)行深入的介紹。

2.3 OAuth協(xié)議 - 基本思路

如圖1所示,協(xié)議的基本流程如下:


(1) Client請(qǐng)求RO的授權(quán),請(qǐng)求中一般包含:要訪問(wèn)的資源路徑,操作類型,Client的身份等信息。

(2) RO批準(zhǔn)授權(quán),并將“授權(quán)證據(jù)”發(fā)送給Client。至于RO如何批準(zhǔn),這個(gè)是協(xié)議之外的事情。典型的做法是,AS提供授權(quán)審批界面,讓RO顯式批準(zhǔn)。這個(gè)可以參考下一節(jié)實(shí)例化分析中的描述。

(3) Client向AS請(qǐng)求“訪問(wèn)令牌(Access Token)”。此時(shí),Client需向AS提供RO的“授權(quán)證據(jù)”,以及Client自己身份的憑證。

(4) AS驗(yàn)證通過(guò)后,向Client返回“訪問(wèn)令牌”。訪問(wèn)令牌也有多種類型,若為bearer類型,那么誰(shuí)持有訪問(wèn)令牌,誰(shuí)就能訪問(wèn)資源。

(5) Client攜帶“訪問(wèn)令牌”訪問(wèn)RS上的資源。在令牌的有效期內(nèi),Client可以多次攜帶令牌去訪問(wèn)資源。

(6) RS驗(yàn)證令牌的有效性,比如是否偽造、是否越權(quán)、是否過(guò)期,驗(yàn)證通過(guò)后,才能提供服務(wù)。

2.4 授權(quán)碼類型的開(kāi)放授權(quán)

如圖2所示,授權(quán)碼類型的開(kāi)放授權(quán)協(xié)議流程描述如下:


(1) Client初始化協(xié)議的執(zhí)行流程。首先通過(guò)HTTP 302來(lái)重定向RO用戶代理到AS。Client在redirect_uri中應(yīng)包含如下參數(shù):client_id, scope (描述被訪問(wèn)的資源), redirect_uri (即Client的URI), state (用于抵制CSRF攻擊). 此外,請(qǐng)求中還可以包含access_type和approval_prompt參數(shù)。當(dāng)approval_prompt=force時(shí),AS將提供交互頁(yè)面,要求RO必須顯式地批準(zhǔn)(或拒絕)Client的此次請(qǐng)求。如果沒(méi)有approval_prompt參數(shù),則默認(rèn)為RO批準(zhǔn)此次請(qǐng)求。當(dāng)access_type=offline時(shí),AS將在頒發(fā)access_token時(shí),同時(shí)還會(huì)頒發(fā)一個(gè)refresh_token。因?yàn)閍ccess_token的有效期較短(如3600秒),為了優(yōu)化協(xié)議執(zhí)行流程,offline方式將允許Client直接持refresh_token來(lái)?yè)Q取一個(gè)新的access_token。

(2) AS認(rèn)證RO身份,并提供頁(yè)面供RO決定是否批準(zhǔn)或拒絕Client的此次請(qǐng)求(當(dāng)approval_prompt=force時(shí))。

(3) 若請(qǐng)求被批準(zhǔn),AS使用步驟(1)中Client提供的redirect_uri重定向RO用戶代理到Client。redirect_uri須包含authorization_code,以及步驟1中Client提供的state。若請(qǐng)求被拒絕,AS將通過(guò)redirect_uri返回相應(yīng)的錯(cuò)誤信息。

(4) Client拿authorization_code去訪問(wèn)AS以交換所需的access_token。Client請(qǐng)求信息中應(yīng)包含用于認(rèn)證Client身份所需的認(rèn)證數(shù)據(jù),以及上一步請(qǐng)求authorization_code時(shí)所用的redirect_uri。

(5) AS在收到authorization_code時(shí)需要驗(yàn)證Client的身份,并驗(yàn)證收到的redirect_uri與第3步請(qǐng)求authorization_code時(shí)所使用的redirect_uri相匹配。如果驗(yàn)證通過(guò),AS將返回access_token,以及refresh_token(若access_type=offline)。

如果讀者對(duì)這個(gè)流程的細(xì)節(jié)不甚清楚,那么可以先看第3節(jié)的一個(gè)實(shí)例化描述,然后再回來(lái)看這部分內(nèi)容。

3. OAuth協(xié)議實(shí)例化描述

下面我以實(shí)例化方式來(lái)幫助讀者理解授權(quán)碼類型的授權(quán)協(xié)議的運(yùn)行過(guò)程。假設(shè):

(1) Alice有一個(gè)有效的Google帳號(hào);

(2) Facebook.com已經(jīng)在Google Authorization Server上注冊(cè)了Client身份,已經(jīng)獲得(client_id, client_secret),注意client_secret是Client與AS之間的一個(gè)共享密鑰。

(3) Alice想授權(quán)Facebook.com查看她的聯(lián)系人列表(https://www.google.com/m8/feeds)。

圖3展示了Alice、Facebook.com、Google資源服務(wù)器、以及Google OAuth授權(quán)服務(wù)器之間的協(xié)議運(yùn)行過(guò)程。



協(xié)議所涉及到的細(xì)節(jié)都已經(jīng)在圖3上了,所以不打算再做詳細(xì)介紹了。若看懂了此圖,OAuth2.0就理解了。

讀者請(qǐng)注意,在步驟(4)中,Client需要拿“授權(quán)碼”去換“授權(quán)令牌”時(shí),Client需要向AS證明自己的身份,即證明自己就是步驟(2)中Alice批準(zhǔn)授權(quán)時(shí)的Grantee。這個(gè)身份證明的方法主要有兩種(圖3中使用了第1種):

(1) 通過(guò)https直接將client_secret發(fā)送給AS,因?yàn)閏lient_secret是由Client與AS所共享,所以只要傳送client_secret的信道安全即可。

(2) 通過(guò)消息認(rèn)證碼來(lái)認(rèn)證Client身份,典型的算法有HMAC-SHA1。在這種方式下,Client無(wú)需傳送client_secret,只需發(fā)送消息請(qǐng)求的signature即可。由于不需要向AS傳遞敏感數(shù)據(jù),所以它只需要使用http即可。

此外,在步驟(2)中,Google授權(quán)服務(wù)器需要認(rèn)證Alice的RO身份,并提供授權(quán)界面給Alice進(jìn)行授權(quán)審批。今天Google提供的實(shí)例如圖4、圖5所示,僅供讀者理解OAuth這種“現(xiàn)場(chǎng)授權(quán)”或"在線授權(quán)"的含義。



4. OAuth設(shè)計(jì)上的安全性考慮

4.1 為何引入authorization_code?

協(xié)議設(shè)計(jì)中,為什么要使用authorization_code來(lái)交換access_token?這是讀者容易想到的一個(gè)問(wèn)題。也就是說(shuō),在協(xié)議的第3步,為什么不直接將access_token通過(guò)重定向方式返回給Client呢?比如:

HTTP/1.1 302

Location:

https://www.facebook.com/?access_token=ya29.AHES6ZSXVKYTW2VAGZtnMjD&token_type=Bearer&expires_in=3600

如果直接返回access_token,協(xié)議將變得更加簡(jiǎn)潔,而且少一次Client與AS之間的交互,性能也更優(yōu)。那為何不這么設(shè)計(jì)呢?協(xié)議文檔[1]中并沒(méi)有給出這樣設(shè)計(jì)的理由,但也不難分析:

(1) 瀏覽器的redirect_uri是一個(gè)不安全信道,此方式不適合于傳遞敏感數(shù)據(jù)(如access_token)。因?yàn)閡ri可能通過(guò)HTTP referrer被傳遞給其它惡意站點(diǎn),也可能存在于瀏覽器cacher或log文件中,這就給攻擊者盜取access_token帶來(lái)了很多機(jī)會(huì)。另外,此協(xié)議也不應(yīng)該假設(shè)RO用戶代理的行為是可信賴的,因?yàn)镽O的瀏覽器可能早已被攻擊者植入了跨站腳本用來(lái)監(jiān)聽(tīng)access_token。因此,access_token通過(guò)RO的用戶代理傳遞給Client,會(huì)顯著擴(kuò)大access_token被泄露的風(fēng)險(xiǎn)。 但authorization_code可以通過(guò)redirect_uri方式來(lái)傳遞,是因?yàn)閍uthorization_code并不像access_token一樣敏感。即使authorization_code被泄露,攻擊者也無(wú)法直接拿到access_token,因?yàn)槟胊uthorization_code去交換access_token是需要驗(yàn)證Client的真實(shí)身份。也就是說(shuō),除了Client之外,其他人拿authorization_code是沒(méi)有用的。 此外,access_token應(yīng)該只頒發(fā)給Client使用,其他任何主體(包括RO)都不應(yīng)該獲取access_token。協(xié)議的設(shè)計(jì)應(yīng)能保證Client是唯一有能力獲取access_token的主體。引入authorization_code之后,便可以保證Client是access_token的唯一持有人。當(dāng)然,Client也是唯一的有義務(wù)需要保護(hù)access_token不被泄露。

(2) 引入authorization_code還會(huì)帶來(lái)如下的好處。由于協(xié)議需要驗(yàn)證Client的身份,如果不引入authorization_code,這個(gè)Client的身份認(rèn)證只能通過(guò)第1步的redirect_uri來(lái)傳遞。同樣由于redirect_uri是一個(gè)不安全信道,這就額外要求Client必須使用數(shù)字簽名技術(shù)來(lái)進(jìn)行身份認(rèn)證,而不能用簡(jiǎn)單的密碼或口令認(rèn)證方式。引入authorization_code之后,AS可以直接對(duì)Client進(jìn)行身份認(rèn)證(見(jiàn)步驟4和5),而且可以支持任意的Client認(rèn)證方式(比如,簡(jiǎn)單地直接將Client端密鑰發(fā)送給AS)。

在我們理解了上述安全性考慮之后,讀者也許會(huì)有豁然開(kāi)朗的感覺(jué),懂得了引入authorization_code的妙處。那么,是不是一定要引入authorization_code才能解決這些安全問(wèn)題呢?當(dāng)然不是。筆者將會(huì)在另一篇博文給出一個(gè)直接返回access_token的擴(kuò)展授權(quán)類型解決方案,它在滿足相同安全性的條件下,使協(xié)議更簡(jiǎn)潔,交互次數(shù)更少。

4.2 基于Web安全的考慮

OAuth協(xié)議設(shè)計(jì)不同于簡(jiǎn)單的網(wǎng)絡(luò)安全協(xié)議的設(shè)計(jì),因?yàn)镺Auth需要考慮各種Web攻擊,比如CSRF (Cross-Site Request Forgery), XSS (Cross Site Script), Clickjacking。要理解這些攻擊原理,讀者需要對(duì)瀏覽器安全(eg, Same Origin Policy, 同源策略)有基本理解。比如,在redirect_uri中引入state參數(shù)就是從瀏覽器安全角度考慮的,有了它就可以抵制CSRF攻擊。如果沒(méi)有這個(gè)參數(shù),攻擊者便可以在redirect_uri中注入攻擊者提供的authorization_code或access_token,結(jié)果可能導(dǎo)致Client訪問(wèn)錯(cuò)誤的資源(比如,將款項(xiàng)匯到一個(gè)錯(cuò)誤的帳號(hào))。

基于Web安全的考慮,OAuth協(xié)議文檔中已經(jīng)有了比較全面的闡述,所以我不打算在此文中進(jìn)行展開(kāi),有興趣的讀者請(qǐng)參考[1]。

5. 結(jié)語(yǔ)

本文對(duì)OAuth 2.0 開(kāi)放授權(quán)協(xié)議及其設(shè)計(jì)上的安全性考慮做了一個(gè)基本的介紹,希望能給參與安全協(xié)議設(shè)計(jì)和開(kāi)發(fā)的同學(xué)起到一點(diǎn)幫助。

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
平臺(tái)聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡(jiǎn)書(shū)系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。
  • 序言:七十年代末,一起剝皮案震驚了整個(gè)濱河市,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌,老刑警劉巖,帶你破解...
    沈念sama閱讀 229,885評(píng)論 6 541
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場(chǎng)離奇詭異,居然都是意外死亡,警方通過(guò)查閱死者的電腦和手機(jī),發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 99,312評(píng)論 3 429
  • 文/潘曉璐 我一進(jìn)店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來(lái),“玉大人,你說(shuō)我怎么就攤上這事。” “怎么了?”我有些...
    開(kāi)封第一講書(shū)人閱讀 177,993評(píng)論 0 383
  • 文/不壞的土叔 我叫張陵,是天一觀的道長(zhǎng)。 經(jīng)常有香客問(wèn)我,道長(zhǎng),這世上最難降的妖魔是什么? 我笑而不...
    開(kāi)封第一講書(shū)人閱讀 63,667評(píng)論 1 317
  • 正文 為了忘掉前任,我火速辦了婚禮,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘。我一直安慰自己,他們只是感情好,可當(dāng)我...
    茶點(diǎn)故事閱讀 72,410評(píng)論 6 411
  • 文/花漫 我一把揭開(kāi)白布。 她就那樣靜靜地躺著,像睡著了一般。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上,一...
    開(kāi)封第一講書(shū)人閱讀 55,778評(píng)論 1 328
  • 那天,我揣著相機(jī)與錄音,去河邊找鬼。 笑死,一個(gè)胖子當(dāng)著我的面吹牛,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播,決...
    沈念sama閱讀 43,775評(píng)論 3 446
  • 文/蒼蘭香墨 我猛地睜開(kāi)眼,長(zhǎng)吁一口氣:“原來(lái)是場(chǎng)噩夢(mèng)啊……” “哼!你這毒婦竟也來(lái)了?” 一聲冷哼從身側(cè)響起,我...
    開(kāi)封第一講書(shū)人閱讀 42,955評(píng)論 0 289
  • 序言:老撾萬(wàn)榮一對(duì)情侶失蹤,失蹤者是張志新(化名)和其女友劉穎,沒(méi)想到半個(gè)月后,有當(dāng)?shù)厝嗽跇?shù)林里發(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 49,521評(píng)論 1 335
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 41,266評(píng)論 3 358
  • 正文 我和宋清朗相戀三年,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點(diǎn)故事閱讀 43,468評(píng)論 1 374
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情,我是刑警寧澤,帶...
    沈念sama閱讀 38,998評(píng)論 5 363
  • 正文 年R本政府宣布,位于F島的核電站,受9級(jí)特大地震影響,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 44,696評(píng)論 3 348
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧,春花似錦、人聲如沸。這莊子的主人今日做“春日...
    開(kāi)封第一講書(shū)人閱讀 35,095評(píng)論 0 28
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽(yáng)。三九已至,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背。 一陣腳步聲響...
    開(kāi)封第一講書(shū)人閱讀 36,385評(píng)論 1 294
  • 我被黑心中介騙來(lái)泰國(guó)打工, 沒(méi)想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留,地道東北人。 一個(gè)月前我還...
    沈念sama閱讀 52,193評(píng)論 3 398
  • 正文 我出身青樓,卻偏偏與公主長(zhǎng)得像,于是被迫代替她去往敵國(guó)和親。 傳聞我的和親對(duì)象是個(gè)殘疾皇子,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 48,431評(píng)論 2 378

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