詳解跨域及CORS解決方案

1.發(fā)現(xiàn)問題

在開發(fā)基于WEB應(yīng)用的時(shí)候爽篷,一般情況下前端負(fù)責(zé)界面展示,后端負(fù)責(zé)業(yè)務(wù)邏輯慢睡,尤其是當(dāng)前分布式逐工、微服務(wù)铡溪、前后端分離架構(gòu)的廣泛使用,前端調(diào)用后端獲取數(shù)據(jù)泪喊,難免會(huì)出現(xiàn)數(shù)據(jù)訪問跨域現(xiàn)象:

當(dāng)瀏覽器console出現(xiàn)下面的提示棕硫,說明出現(xiàn)了跨域問題。

Access to XMLHttpRequest at 'http://www.float.com/goods/findPage?page=1&size=5' from origin 'www.integer.net.cn' has been blocked by CORS policy: No 'Access-Control-Allow-Origin' header is present on the requested resource.

關(guān)鍵詞:

origin:域袒啼、源

CORS:全稱是"跨域資源共享"(Cross-Origin Resource Sharing)

blocked:阻止哈扮,阻塞

Access-Control-Allow-Origin:HTTP協(xié)議中與跨域有關(guān)的請求頭信息。

CORS policy:CORS策略

接下來有個(gè)問題蚓再,域是什么鬼滑肉?

2.什么是域

當(dāng)一個(gè)請求url的協(xié)議、域名摘仅、端口三者之間任意一個(gè)與當(dāng)前頁面url不同即為不同的域

3.為什么會(huì)出現(xiàn)跨域問題

出于安全考慮靶庙,瀏覽器使用同源策略(SameOriginPolicy)進(jìn)行訪問的限制。

所謂同源(或同一個(gè)域)就是兩個(gè)請求具有相同的協(xié)議(protocol)娃属,主機(jī)(host)和端口號(port)六荒,這三者均相同則表示這兩個(gè)請求同源。

同源策略防止當(dāng)前域的javascript腳本和另外一個(gè)域的內(nèi)容進(jìn)行交互矾端,這就是為什么出現(xiàn)跨域訪問被阻止的原因掏击。

同源策略是Web應(yīng)用程序安全模型中的一個(gè)重要概念,它由 Netscape 公司在1995年引入瀏覽器。該策略最初是為保護(hù)DOM的訪問而設(shè)計(jì)的秩铆,Web瀏覽器允許第一個(gè)Web頁面中包含的腳本訪問第二個(gè)Web頁面中的數(shù)據(jù)砚亭,但前提是兩個(gè)Web頁面具有相同的源(origin)。后來進(jìn)行了擴(kuò)展殴玛,此策略可防止一個(gè)頁面上的惡意腳本(ajax請求)訪問另一個(gè)網(wǎng)頁上的敏感數(shù)據(jù)钠惩。

惡意腳本訪問敏感數(shù)據(jù)

目前Web應(yīng)用程序廣泛依賴HTTP cookie來維護(hù)經(jīng)過身份驗(yàn)證的用戶會(huì)話,服務(wù)器基于cookie信息來顯示或者獲取敏感信息族阅。客戶端必須保證無關(guān)站點(diǎn)與當(dāng)前站點(diǎn)嚴(yán)格分離膝捞,以防止數(shù)據(jù)機(jī)密性或完整性丟失坦刀。

同源策略主要防止一個(gè)域的javascript腳本和另外一個(gè)域的內(nèi)容進(jìn)行交互。

主要限制包括:

不同源的頁面之間Cookie蔬咬、LocalStorage 和 IndexDB無法共享

不同源AJAX 請求不能發(fā)送

通過相應(yīng)的HTML標(biāo)記嵌入的資源不受限制鲤遥,如圖像,CSS和腳本

圖像: <img src="http://abc.com/1.png"/>

CSS: <link rel="stylesheet" type="text/css" />

腳本: <script type="text/javascript" src="http://abc.com/a.js"></script>

(1)當(dāng)訪問http://www.integer.net.cn的時(shí)候林艘,瀏覽器發(fā)送GET 請求獲取主頁盖奈,這時(shí)候當(dāng)前頁面的域指定為http://www.integer.net.cn

(2)通過<img src="http://www.integer.net.cn/1.png">訪問1.png圖片,沒有跨域狐援,允許訪問钢坦,通過<img src="http://www.double.net.cn/2.png">訪問2.png圖片究孕,雖然跨域,但訪問的是HTML嵌入資源爹凹,不受同源策略影響厨诸,依然可以訪問。

(3)通過<link />訪問a.css禾酱,沒有跨域微酬,允許訪問,通過< link >訪問b.css颤陶,雖然跨域颗管,但訪問的是HTML嵌入資源,不受同源策略影響滓走。

(4)使用ajax異步請求http://www.integer.net.cn域下的數(shù)據(jù)垦江,沒有跨域,允許訪問

(5)使用ajax異步請求http://www.float.com域下的數(shù)據(jù)闲坎,此時(shí)跨域疫粥,同源策略阻止繼續(xù)訪問,是否允許訪問由CORS策略控制腰懂。

從以上分析可以看出梗逮,只有當(dāng)ajax異步請求跨域時(shí),同源策略才會(huì)起作用绣溜,跨域請求就一定不能訪問呢慷彤?當(dāng)然不是,上面已經(jīng)提及怖喻,是否能訪問跨域資源由CORS策略決定的底哗,那么什么是CORS策略呢?

4.CORS跨域解決方案(服務(wù)端)

CORS策略锚沸,它允許瀏覽器向跨域服務(wù)器發(fā)出XMLHttpRequest(AJAX)請求跋选,從而克服了AJAX只能同源訪問的限制。

整個(gè)CORS通信過程哗蜈,前端都是瀏覽器自動(dòng)完成前标,不需要用戶參與。對于開發(fā)者來說距潘,CORS通信與同源的AJAX通信沒有差別炼列,代碼完全一樣。瀏覽器一旦發(fā)現(xiàn)AJAX請求跨域音比,就會(huì)自動(dòng)添加一些附加的頭信息(針對簡單請求)俭尖,有時(shí)還會(huì)多出一次附加的請求(針對復(fù)雜請求),但用戶不會(huì)有感覺。因此稽犁,實(shí)現(xiàn)CORS通信的關(guān)鍵是服務(wù)器焰望。只要服務(wù)器實(shí)現(xiàn)了CORS接口,就可以跨域通信缭付。

上面這段話柿估,翻譯成大白話就是瀏覽器發(fā)現(xiàn)AJAX請求跨域,添加附加信息(簡單請求)或者附加請求(復(fù)雜請求)發(fā)送到服務(wù)器陷猫,如果服務(wù)器端允許當(dāng)前訪問秫舌,則繼續(xù)訪問返回響應(yīng)結(jié)果,如果服務(wù)器不允許該來源訪問绣檬,則會(huì)出現(xiàn)跨域訪問現(xiàn)象足陨。所以,主動(dòng)權(quán)掌握在服務(wù)器手里娇未。這就是CORS策略墨缘。

就像你想要約隔壁班的女生看電影,人家和你不是一個(gè)班根本不認(rèn)識你零抬,去不去由人家決定镊讼。當(dāng)然,附加的信息就是你表明自己身份去邀請人家平夜,這里還有一個(gè)問題蝶棋,如果只是看電影這種簡單請求,你拿著票表達(dá)誠意就行忽妒。但是對于其他復(fù)雜請求玩裙,問一次肯定不行,先要進(jìn)行一次預(yù)請求段直,試探一下吃溅,試探成功后再進(jìn)行復(fù)雜請求。

那么哪些請求是簡單請求鸯檬,哪些是復(fù)雜請求呢决侈?

簡單請求需要滿足以下幾點(diǎn):

請求是GET、POST喧务、HEAD三種

HTTP的頭信息不超出以下幾種字段 Accept颜及、Accept-Language、Content-Language蹂楣、Content-Type

Content-Type 的值僅限于下列三者之一:text/plain、multipart/form-data讯蒲、application/x-www-form-urlencoded

什么是復(fù)雜請求呢痊土,除了簡單請求都是復(fù)雜請求。

比如說你需要發(fā)送PUT墨林、DELETE等HTTP請求赁酝,或者發(fā)送Content-Type: application/json的內(nèi)容犯祠,這些就都是復(fù)雜請求。

那么簡單請求中附加的信息是什么呢酌呆?

跨域請求HTTP頭當(dāng)中要求包含一個(gè)域(Origin)的信息衡载。該域包含協(xié)議名、地址以及一個(gè)可選的端口隙袁。不過這一項(xiàng)實(shí)際上由瀏覽器代為發(fā)送痰娱,開發(fā)者不需要操作,這就是簡單請求附加的信息菩收。

服務(wù)器端對簡單請求進(jìn)行響應(yīng)梨睁,部分響應(yīng)頭及解釋如下:

Access-Control-Allow-Origin(必含) - 不可省略,否則請求按失敗處理娜饵。該項(xiàng)控制數(shù)據(jù)的可見范圍坡贺,如果希望數(shù)據(jù)對任何人都可見,可以填寫"*"箱舞。

Access-Control-Allow-Credentials(可選) - 該項(xiàng)標(biāo)志著請求當(dāng)中是否包含cookies信息遍坟,只有一個(gè)可選值:true(必為小寫)。如果不包含cookies晴股,請略去該項(xiàng)愿伴,而不是填寫false。這一項(xiàng)與XmlHttpRequest對象(ajax請求)當(dāng)中的withCredentials屬性應(yīng)保持一致队魏,即withCredentials為true時(shí)該項(xiàng)也為true公般;withCredentials為false時(shí),省略該項(xiàng)不寫胡桨。反之則導(dǎo)致請求失敗官帘。

簡單請求

任何不滿足上述簡單請求要求的請求,都屬于復(fù)雜請求昧谊。

復(fù)雜請求會(huì)在正式通信之前刽虹,增加一次HTTP查詢請求,稱為"預(yù)檢"請求(preflight)呢诬。預(yù)檢請求為OPTIONS請求涌哲,用于向服務(wù)器請求權(quán)限信息的。預(yù)檢請求被成功響應(yīng)后尚镰,才會(huì)發(fā)出真實(shí)請求阀圾,攜帶真實(shí)數(shù)據(jù)。

復(fù)雜請求

預(yù)請求以O(shè)PTIONS形式發(fā)送狗唉,當(dāng)中同樣包含域初烘,并且還包含了兩項(xiàng)CORS特有的內(nèi)容:

Access-Control-Request-Method – 該項(xiàng)內(nèi)容是實(shí)際請求的種類,可以是GET、POST之類的簡單請求肾筐,也可以是PUT哆料、DELETE等等。

Access-Control-Request-Headers – 該項(xiàng)是一個(gè)以逗號分隔的列表吗铐,當(dāng)中是復(fù)雜請求所使用的頭部东亦。

顯而易見,這個(gè)預(yù)請求實(shí)際上就是在為之后的實(shí)際請求發(fā)送一個(gè)權(quán)限請求唬渗,在預(yù)回應(yīng)返回的內(nèi)容當(dāng)中典阵,服務(wù)端應(yīng)當(dāng)對這兩項(xiàng)進(jìn)行回復(fù),以讓瀏覽器確定請求是否能夠成功完成谣妻。

復(fù)雜請求的部分響應(yīng)頭及解釋如下:

Access-Control-Allow-Origin(必含) – 和簡單請求一樣的萄喳,必須包含一個(gè)域。

Access-Control-Allow-Methods(必含) – 這是對預(yù)請求當(dāng)中Access-Control-Request-Method的回復(fù)蹋半,這一回復(fù)將是一個(gè)以逗號分隔的列表他巨。盡管客戶端或許只請求某一方法,但服務(wù)端仍然可以返回所有允許的方法减江,以便客戶端將其緩存染突。

Access-Control-Allow-Headers(當(dāng)預(yù)請求中包含Access-Control-Request-Headers時(shí)必須包含) – 這是對預(yù)請求當(dāng)中Access-Control-Request-Headers的回復(fù),和上面一樣是以逗號分隔的列表辈灼,可以返回所有支持的頭部份企。這里在實(shí)際使用中有遇到,所有支持的頭部一時(shí)可能不能完全寫出來巡莹,而又不想在這一層做過多的判斷司志,沒關(guān)系,事實(shí)上通過request的header可以直接取到Access-Control-Request-Headers降宅,直接把對應(yīng)的value設(shè)置到Access-Control-Allow-Headers即可骂远。

Access-Control-Allow-Credentials(可選) – 和簡單請求當(dāng)中作用相同。

Access-Control-Max-Age(可選) – 以秒為單位的緩存時(shí)間腰根。預(yù)請求的的發(fā)送并非免費(fèi)午餐激才,允許時(shí)應(yīng)當(dāng)盡可能緩存。

一旦預(yù)回應(yīng)如期而至额嘿,所請求的權(quán)限也都已滿足瘸恼,則實(shí)際請求開始發(fā)送。

5.Spring CORS –@CrossOrigin注解

Spring MVC 提供@CrossOrigin 注解. 該注解可以標(biāo)注在類或者方法上

5.1. Spring CORS 配置

@CrossOrigin?默認(rèn)將允許所有的源册养,所有頭信息东帅,@RequestMapping標(biāo)注的方法進(jìn)行跨域訪問,maxAge 為30分鐘球拦。

CrossOrigin 配置參數(shù)

5.2. @CrossOrigin 注解標(biāo)注在Controller類

@CrossOrigin(origins ="*", allowedHeaders ="*")

@Controller

publicclassHomeController{

????@GetMapping(path="/")

????public String homeInit(Model model) {

????????return"home";

????}

}

表明該類的所有方法靠闭,均實(shí)現(xiàn)CORS策略邓夕。

5.3. @CrossOrigin 注解標(biāo)注在方法

@Controller

publicclassHomeController{

????@CrossOrigin(origins ="*", allowedHeaders ="*")

????@GetMapping(path="/")

????public String homeInit(Model model) {

????????return"home";

????}

}

表明該方法實(shí)現(xiàn)CORS策略

5.4. @CrossOrigin 在方法級覆蓋類級注解

@Controller

@CrossOrigin(origins ="*", allowedHeaders ="*")

public class HomeController{

????@CrossOrigin(origins ="http://example.com")

????@GetMapping(path="/")

????publicString homeInit(Model model) {

????????return"home";

????}

}

homeInit() 方法只能被域 http://example.com訪問,HomeController 的其他方法可以被所有的域

6.Spring CORS – 全局配置

6.1. 實(shí)現(xiàn)WebMvcConfigurer

為了讓整個(gè)應(yīng)該所有的請求均實(shí)現(xiàn)CORS跨域方案, 可以使用WebMvcConfigurer 并添加到 CorsRegistry中阎毅。

import org.springframework.context.annotation.Configuration;

import rg.springframework.web.servlet.config.annotation.CorsRegistry;

import org.springframework.web.servlet.config.annotation.EnableWebMvc;

import org.springframework.web.servlet.config.annotation.WebMvcConfigurer;

@Configuration

@EnableWebMvc

publicclass CorsConfiguration implements WebMvcConfigurer{

@Override

public void addCorsMappings(CorsRegistry registry) {

????????????registry.addMapping("/**").allowedMethods("GET", "POST");

? ? }

}

6.2. WebMvcConfigurer Bean

spring boot應(yīng)用中, 推薦聲明一個(gè) WebMvcConfigurer bean.

@Configuration

publicclassCorsConfiguration{

@Bean

publicWebMvcConfigurercorsConfigurer() {

????????return new WebMvcConfigurer() {

????????@Override

????????public void addCorsMappings(CorsRegistry registry){

? ?????????????registry.addMapping("/**").allowedOrigins("http://localhost:8080");

????}? ? ? ?

?};? ??

}

}

6.3 CORS 與Spring Security

為了能夠使CORS 支持 Spring security, 配置CorsConfigurationSource bean 并且使用 HttpSecurity.cors() 進(jìn)行設(shè)置。

@EnableWebSecurity

publicclassWebSecurityConfigextendsWebSecurityConfigurerAdapter{

@Override

protected void configure(HttpSecurity http)throwsException{

????????http.cors().and()//other config

? ? }

? ? @Bean

? ? CorsConfigurationSource corsConfigurationSource() {

? ? ? ? CorsConfiguration configuration = new CorsConfiguration();

? ? ? ? configuration.setAllowedOrigins(Arrays.asList("https://example.com"));

? ? ? ? configuration.setAllowedMethods(Arrays.asList("GET","POST"));

? ? ? ? UrlBasedCorsConfigurationSource source = new UrlBasedCorsConfigurationSource();

? ? ? ? source.registerCorsConfiguration("/**", configuration);

? ? ? ? return source;

? ? }

}

7.Java CORS過濾器

如果應(yīng)用中沒有集成Spring或者SpringBoot框架点弯,可以直接使用過濾器來進(jìn)行配置扇调,基本原理就是對Http 頭信息就行設(shè)置。

7.1Response Headers

·?Access-Control-Allow-Origin?: 指示允許哪些域進(jìn)行跨域訪問. “*” 代表沒有限制.

·?Access-Control-Allow-Credentials?: 指示跨域訪問是否支持用戶身份抢肛,即cookie.

·?Access-Control-Expose-Headers?: 指示哪些頭信息暴露出來.

·?Access-Control-Max-Age?: 指示預(yù)請求響應(yīng)在客戶端緩存的時(shí)間.

·?Access-Control-Allow-Methods?: 指示允許哪些方法可以訪問資源

·?Access-Control-Allow-Headers?:指示在實(shí)際請求中哪些頭信息被允許.

7.2. Request Headers

·?Origin?: 指示跨域請求或者預(yù)請求的來源.

·?Access-Control-Request-Method?: 在預(yù)請求中使用狼钮,告訴服務(wù)器實(shí)際請求講使用該方法

·?Access-Control-Request-Headers?:在預(yù)請求中使用,告訴服務(wù)器實(shí)際請求中將帶的頭信息.

7.3. Java CORS 過濾器demo

import java.io.IOException;

import javax.servlet.Filter;

import javax.servlet.FilterChain;

import javax.servlet.FilterConfig;

import javax.servlet.ServletException;

import javax.servlet.ServletRequest;

import javax.servlet.ServletResponse;

import javax.servlet.annotation.WebFilter;

import javax.servlet.http.HttpServletRequest;

import javax.servlet.http.HttpServletResponse;

/**

* Servlet Filter implementation class CORSFilter

*/

// Enable it for Servlet 3.x implementations

/* @ WebFilter(asyncSupported = true, urlPatterns = { "/*" }) */

public class CORSFilter implements Filter {

? ? /**

? ? * Default constructor.

? ? */

? ? public CORSFilter() {

? ? ? ? // TODO Auto-generated constructor stub

? ? }

? ? /**

? ? * @see Filter#destroy()

? ? */

? ? public void destroy() {

? ? ? ? // TODO Auto-generated method stub

? ? }

? ? /**

? ? * @see Filter#doFilter(ServletRequest, ServletResponse, FilterChain)

? ? */

? ? public void doFilter(ServletRequest servletRequest,ServletResponse servletResponse, FilterChain chain)

? ? ? ? ? ? throws IOException, ServletException {

? ? ? ? HttpServletRequest request = (HttpServletRequest) servletRequest;

? ? ? ? System.out.println("CORSFilter HTTP Request: " + request.getMethod());

? ? ? ? // Authorize (allow) all domains to consume the content

? ? ? ? ((HttpServletResponse) servletResponse).addHeader("Access-Control-Allow-Origin", "*");

? ? ? ? ((HttpServletResponse) servletResponse)

????????????????????.addHeader("Access-Control-Allow-Methods","GET, OPTIONS, HEAD, PUT, POST");

? ? ? ? HttpServletResponse resp = (HttpServletResponse) servletResponse;

? ? ? ? // For HTTP OPTIONS verb/method reply with ACCEPTED status code -- per CORS handshake

? ? ? ? if (request.getMethod().equals("OPTIONS")) {

? ? ? ? ? ? resp.setStatus(HttpServletResponse.SC_ACCEPTED);

? ? ? ? ? ? return;

? ? ? ? }

? ? ? ? // pass the request along the filter chain

? ? ? ? chain.doFilter(request, servletResponse);

? ? }

? ? /**

? ? * @see Filter#init(FilterConfig)

? ? */

? ? public void init(FilterConfig fConfig) throws ServletException {

? ? ? ? // TODO Auto-generated method stub

? ? }

}

web.xml配置

<filter>

????<filter-name>CorsFilter</filter-name>

????<filter-class>com.howtodoinjava.examples.cors.CORSFilter</filter-class>

</filter>

<filter-mapping>

????<filter-name>CorsFilter</filter-name>

????<url-pattern>/*</url-pattern>

</filter-mapping>

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末捡絮,一起剝皮案震驚了整個(gè)濱河市熬芜,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌福稳,老刑警劉巖涎拉,帶你破解...
    沈念sama閱讀 222,104評論 6 515
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場離奇詭異的圆,居然都是意外死亡鼓拧,警方通過查閱死者的電腦和手機(jī),發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 94,816評論 3 399
  • 文/潘曉璐 我一進(jìn)店門越妈,熙熙樓的掌柜王于貴愁眉苦臉地迎上來季俩,“玉大人,你說我怎么就攤上這事梅掠∽米。” “怎么了?”我有些...
    開封第一講書人閱讀 168,697評論 0 360
  • 文/不壞的土叔 我叫張陵阎抒,是天一觀的道長酪我。 經(jīng)常有香客問我,道長挠蛉,這世上最難降的妖魔是什么祭示? 我笑而不...
    開封第一講書人閱讀 59,836評論 1 298
  • 正文 為了忘掉前任,我火速辦了婚禮谴古,結(jié)果婚禮上质涛,老公的妹妹穿的比我還像新娘。我一直安慰自己掰担,他們只是感情好汇陆,可當(dāng)我...
    茶點(diǎn)故事閱讀 68,851評論 6 397
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著带饱,像睡著了一般毡代。 火紅的嫁衣襯著肌膚如雪阅羹。 梳的紋絲不亂的頭發(fā)上,一...
    開封第一講書人閱讀 52,441評論 1 310
  • 那天教寂,我揣著相機(jī)與錄音捏鱼,去河邊找鬼。 笑死酪耕,一個(gè)胖子當(dāng)著我的面吹牛导梆,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播迂烁,決...
    沈念sama閱讀 40,992評論 3 421
  • 文/蒼蘭香墨 我猛地睜開眼看尼,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了盟步?” 一聲冷哼從身側(cè)響起藏斩,我...
    開封第一講書人閱讀 39,899評論 0 276
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎却盘,沒想到半個(gè)月后狰域,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 46,457評論 1 318
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡谷炸,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 38,529評論 3 341
  • 正文 我和宋清朗相戀三年北专,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片旬陡。...
    茶點(diǎn)故事閱讀 40,664評論 1 352
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡拓颓,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出描孟,到底是詐尸還是另有隱情驶睦,我是刑警寧澤,帶...
    沈念sama閱讀 36,346評論 5 350
  • 正文 年R本政府宣布匿醒,位于F島的核電站场航,受9級特大地震影響,放射性物質(zhì)發(fā)生泄漏廉羔。R本人自食惡果不足惜溉痢,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 42,025評論 3 334
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望憋他。 院中可真熱鬧孩饼,春花似錦、人聲如沸竹挡。這莊子的主人今日做“春日...
    開封第一講書人閱讀 32,511評論 0 24
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽揪罕。三九已至梯码,卻和暖如春宝泵,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背轩娶。 一陣腳步聲響...
    開封第一講書人閱讀 33,611評論 1 272
  • 我被黑心中介騙來泰國打工儿奶, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留,地道東北人鳄抒。 一個(gè)月前我還...
    沈念sama閱讀 49,081評論 3 377
  • 正文 我出身青樓廓握,卻偏偏與公主長得像,于是被迫代替她去往敵國和親嘁酿。 傳聞我的和親對象是個(gè)殘疾皇子,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 45,675評論 2 359