UIWebView和WKWebView的cookie管理机制

Cookie

  • 关于 cookie
    cookie 是服务端为了识别终端身份,保存在终端本地的用户凭证信息。cookie 中的字段与意义由服务端进行定义。例如,当用户在进行了登录操作后,服务端会将cookie 信息返回给终端,终端会将这些信息进行保存,在下一次再次访问服务端时,终端会将保存的cookie 信息一并发送到服务端,服务端根据cookie 信息是否有效来判断此用户是否可以进行一些行为。

  • 在 iOS 中如何管理cookie
    iOS 中Cookie管理主要有两个类 NSHTTPCookieNSHTTPCookieStorage,当你访问一个网站时,NSURLRequest都会帮你主动记录下来你访问的站点设置的Cookie,因为NSHTTPCookieStorage的默认策略是:NSHTTPCookieAcceptPolicyAlways,所以如果 Cookie 存在的话,会把这些信息放在 NSHTTPCookieStorage 容器中共享,当你下次再访问这个站点时,NSURLRequest会拿着上次保存下来了的Cookie继续去请求。

NSHTTPCookieStorage介绍
NSHTTPCookieStorage,管理着所有HTTP请求的Cookie信息.

继承自:NSObject
遵守协议:NSObject
导入声明:@import Foundation;
适用范围:iOS 2.0 及以后

官方解释:NSHTTPCookieStorage 是一个用来管理 cookie 存储的单例。一个 NSHTTPCookie 单例代表一个 cookie。通常来讲,cookie 可以在应用间共享,并且在进程之间保持同步。 对于单进程,Session cookies (这里的 cookie 对象的 isSessionOnly 方法返回 YES)是局部的并且不能被共享。

在 iOS 中,应用间是不能共享 cookie 的。
在 OS X 10.9 及以后和 iOS 7 及以后,NSHTTPCookieStorage 是线程安全的。

// 获取单例对象
+ (NSHTTPCookieStorage *)sharedHTTPCookieStorage;

// 所有Cookie数据数组 其中存放NSHTTPCookie对象
@property (nullable , readonly, copy) NSArray<NSHTTPCookie *> *cookies;

// 手动设置一条Cookie数据
- (void)setCookie:(NSHTTPCookie *)cookie;

// 删除某条Cookie信息
- (void)deleteCookie:(NSHTTPCookie *)cookie;

// 删除某个时间后的所有Cookie信息 iOS8后可用
- (void)removeCookiesSinceDate:(NSDate *)date NS_AVAILABLE(10_10, 8_0);

// 获取某个特定URL的所有Cookie数据
- (nullable NSArray<NSHTTPCookie *> *)cookiesForURL:(NSURL *)URL;

// 为某个特定的URL设置Cookie
- (void)setCookies:(NSArray<NSHTTPCookie *> *)cookies forURL:(nullable NSURL *)URL mainDocumentURL:(nullable NSURL *)mainDocumentURL;

// Cookie数据的接收协议
枚举如下:
typedef NS_ENUM(NSUInteger, NSHTTPCookieAcceptPolicy) {
    NSHTTPCookieAcceptPolicyAlways,//接收所有Cookie信息
    NSHTTPCookieAcceptPolicyNever,//不接收所有Cookie信息
    NSHTTPCookieAcceptPolicyOnlyFromMainDocumentDomain//只接收主文档域的Cookie信息
};
@property NSHTTPCookieAcceptPolicy cookieAcceptPolicy;

系统下面的两个通知与Cookie管理有关:
据说,在Mac OS是cookie可以共享的(Session cookies 不能共享),在Mac OS app中更改cookie的接收策略会影响到其他正在运行的在使用cookie storage的app.这时NSHTTPCookieStorage会发出两个通知:
// Cookie数据的接收协议改变时发送的通知
FOUNDATION_EXPORT NSString * const NSHTTPCookieManagerAcceptPolicyChangedNotification;
// 管理的Cookie数据发生变化时发送的通知
FOUNDATION_EXPORT NSString * const NSHTTPCookieManagerCookiesChangedNotification;

NSHTTPCookie介绍
NSHTTPCookie是具体的HTTP请求Cookie数据对象.

// 下面两个方法用于对象的创建和初始化 都是通过字典进行键值设置
- (nullable instancetype)initWithProperties:(NSDictionary<NSString *, id> *)properties;
+ (nullable NSHTTPCookie *)cookieWithProperties:(NSDictionary<NSString *, id> *)properties;

// 返回Cookie数据中可用于添加HTTP头字段的字典
+ (NSDictionary<NSString *, NSString *> *)requestHeaderFieldsWithCookies:(NSArray<NSHTTPCookie *> *)cookies;

// 从指定的响应头和URL地址中解析出Cookie数据
+ (NSArray<NSHTTPCookie *> *)cookiesWithResponseHeaderFields:(NSDictionary<NSString *, NSString *> *)headerFields forURL:(NSURL *)URL;

// Cookie数据中的属性字典
@property (nullable, readonly, copy) NSDictionary<NSString *, id> *properties;

// 请求响应的版本
@property (readonly) NSUInteger version;

// 请求相应的名称
@property (readonly, copy) NSString *name;

// 请求相应的值
@property (readonly, copy) NSString *value;

// 过期时间
@property (nullable, readonly, copy) NSDate *expiresDate;

// 请求的域名
@property (readonly, copy) NSString *domain;

//请求的路径
@property (readonly, copy) NSString *path;

// 是否是安全传输
@property (readonly, getter=isSecure) BOOL secure;

// 是否只发送HTTP的服务
@property (readonly, getter=isHTTPOnly) BOOL HTTPOnly;

// 响应的文档
@property (nullable, readonly, copy) NSString *comment;

// 相应的文档URL
@property (nullable, readonly, copy) NSURL *commentURL;

// 服务端口列表
@property (nullable, readonly, copy) NSArray<NSNumber *> *portList;

HTTP cookie的属性键

属性 解读
NSHTTPCookieName Cookie的名字
NSHTTPCookieValue Cookie的值
NSHTTPCookieOriginURL 和域名一样,NSHTTPCookieDomain或NSHTTPCookieOriginURL必须指定一个值
NSHTTPCookieVersion 接收器的版本
NSHTTPCookieDomain 域名
NSHTTPCookiePath Cookie 存放路径
NSHTTPCookieSecure Cookie是否只应通过安全通道发送,设置Cookie的secure属性为true。只会在HTTPS和SSL等安全协议中传输此类Cookie。默认为false
NSHTTPCookieComment 包含Cookie的评论,只有有效的版本1的cookies或更高版本。 这头字段是可选的
NSHTTPCookieCommentURL 接收器的评论URL
NSHTTPCookieDiscard Cookie是否应在会议结束时丢弃NSString,字符串值必须是“true”或“假”。 这个字段是可选的。 默认为“假”,除非这是Cookie是第1版或以上,NSHTTPCookieMaximumAge未指定,在这种情况下,它被假定为“TRUE”
NSHTTPCookieMaximumAge NSString对象,包含一个整数,在Cookie内保持最多几秒 。仅适用于第1版和更高版本的有效。 默认为“0”。 此字段是可选的
NSHTTPCookiePort 接收机的端口

清除Cookie步骤

// 清除所有的cookie 方法:
NSURL *url = [NSURL URLWithString:@"http://www.baidu.com"];  
    if (url) {  
        NSArray *cookies = [[NSHTTPCookieStorage sharedHTTPCookieStorage] cookiesForURL:url];  
        for (int i = 0; i < [cookies count]; i++) {  
            NSHTTPCookie *cookie = (NSHTTPCookie *)[cookies objectAtIndex:i];  
            [[NSHTTPCookieStorage sharedHTTPCookieStorage] deleteCookie:cookie];  
        }  
  }  

// 清除某一个特定的cookie方法:
NSArray * cookArray = [[NSHTTPCookieStorage sharedHTTPCookieStorage] cookiesForURL:[NSURL URLWithString:self.loadURL]]; 
NSString * successCode = @""; 
for (NSHTTPCookie*cookie in cookArray) { 
  if ([cookie.name isEqualToString:@"cookiename"]) { 
      [[NSHTTPCookieStorage sharedHTTPCookieStorage] deleteCookie:cookie]; 
  } 
} 

// 清除某一个url缓存的方法:
[[NSURLCache sharedURLCache] removeCachedResponseForRequest:[NSURLRequest requestWithURL:url]]; 

// 清除所有缓存方法:        
[[NSURLCache sharedURLCache] removeAllCachedResponses]; 
  • 在iOS中如何使用cookie

(1) 可以在 App 启动时或者在webview的viewDidLoad中判断用户是否登录,本地是否记录了登录后的cookie,并且cookie是否失效,如果是登录状态,本地无cookie,可以手动自定义cookie,并存入NSHTTPCookieStorage中,如果本地有cookie记录,判断cookie是否失效,设置有效的cookie数据。
(2) 在UIWebView或者WKWebView的加载前的代理方法中判断是否已经登录,如果没有登录,则直接跳转到app的登录页面。在登录成功之后发送通知,在webview中进行页面刷新。
(3) 在登录完成时存 cookie 。
(4) 在退出时清除 cookie 。

// 可以在登录完成时,或者已登录但本地没有记录cookie时设置
// 如果服务器没有定义cookie,则可以本地自定义
// 自定义cookie:手动设置的Cookie不会自动持久化到沙盒,需要利用NSUserDefaults进行存储
    NSMutableDictionary*cookieProperties = [NSMutableDictionary dictionary];
    [cookieProperties setObject:@"token"  forKey:NSHTTPCookieName]; //给cookie取名
    NSString * token =[[NSUserDefaults standardUserDefaults] objectForKey:@"token"];
    [cookieProperties setObject:token forKey:NSHTTPCookieValue];
// 存放目录
    [cookieProperties setObject:@"/" forKey:NSHTTPCookiePath]; 
    NSString * domain = [NSURL URLWithString:@"http://www.baidu.com/"].host;
    [cookieProperties setObject:domain forKey:NSHTTPCookieDomain]; //设置域名
// 将可变字典转化为cookie
    NSHTTPCookie * httpCookie = [NSHTTPCookie cookieWithProperties:cookieProperties];
// 将cookie存入仓库
    [[NSHTTPCookieStorage sharedHTTPCookieStorage] setCookie:httpCookie]; 
    NSData *cookiesData = [NSKeyedArchiver archivedDataWithRootObject: httpCookie];
    [[NSUserDefaults standardUserDefaults]  setObject:cookiesData forKey:@"cookie"];
    [[NSUserDefaults standardUserDefaults]  synchronize];
//生成的cookie:version: 0  name:token   value : token值  expiresDate:cookie有效截止日期  created :创建日期  sessionOnly: 返回接收器是否应该在会话结束时被丢弃 domain :域名  partition: 端口  path:路径  isSecure:返回他的cookie不只应通过安全通道发送
<NSHTTPCookie version:0 name:"token" value:"b2a8f8523ab41f8b4b9b2a79ff47c3f1" expiresDate:(null) created:2016-11-09 03:02:41 +0000 sessionOnly:TRUE domain:"api.test.com" partition:"none" path:"/" isSecure:FALSE>
  • cookie的持久化存储
    对于获取服务器直接返回的cookie,或者手动定义的cookie

如果设置了Cookie失效时间expiresDate ,sessionOnly:FALSE,会被持久化到文件中,kill掉后系统自动保存,下次启动app会自动加载沙盒的 /Library/Cookies中的Cookies

如果没有设置Cookie失效时间expiresDate:(null),sessionOnly:TURE,kill掉后系统不会自动保存Cookie,如果想持久化Cookie 模仿浏览器存住Cookie,可以使用NSUserDefaults进行本地存储

UIWebView的cookie机制

UIWebView会将NSHttpRequest的所有请求产生的cookie自动保存到NSHTTPCookieStorage容器中,并且在同一个app内多个UIWebView之间共享,不需要我们做任何操作,在后续访问中会将 cookie 自动带到 request 请求当中。
UIWebView 在浏览网页后会将网页中的 cookie 自动存入 NSHTTPCookieStorage 标准容器中。在后续访问中会将 cookie 自动带到 request 请求当中。比如,NSHTTPCookieStorage 中存储了一个Cookie,name=a;value=b;domain=y.qq.com;expires=Sat,02 May 2017 23:20:25 GMT; 则通 过 UIWebView 发起请求 http://y.qq.com,则请求头会自动带上cookie,而通过 WKWebView 发起请求,请求头不会自动带上该cookie。

WKWebView的cookie机制

业界普遍认为 WKWebView 拥有自己的私有存储,不会将 Cookie 存入到标准的 Cookie 容器 NSHTTPCookieStorage 中。

实践发现 WKWebView 实例其实也会将 Cookie 存储于 NSHTTPCookieStorage 中,但存储时机有延迟,在iOS 8上,当页面跳转的时候,当前页面的 Cookie 会写入 NSHTTPCookieStorage 中,而在 iOS 10 上,JS 执行 document.cookie 或服务器 set-cookie 注入的 Cookie 会很快同步到 NSHTTPCookieStorage 中,FireFox 工程师曾建议通过 reset WKProcessPool 来触发 Cookie 同步到 NSHTTPCookieStorage 中,实践发现不起作用,并可能会引发当前页面 session cookie 丢失等问题。

WKWebView Cookie 问题在于 WKWebView 发起的请求不会自动带上存储于 NSHTTPCookieStorage 容器中的 Cookie。
比如,NSHTTPCookieStorage 中存储了一个 Cookie:

 name=Nicholas;value=test;domain=y.qq.com;expires=Sat, 02 May 2019 23:38:25 GMT;

通过 UIWebView 发起请求http://y.qq.com/, 则请求头会自动带上 cookie: Nicholas=test;
而通过 WKWebView发起请求http://y.qq.com/, 请求头不会自动带上 cookie: Nicholas=test。

WKWebView中注入Cookie
如果你在Native层面做了登陆操作,获取了Cookie信息,也使用 NSHTTPCookieStorage 存到了本地,但是使用 WKWebView 打开对应网页时,网页依然处于未登陆状态。如果是登陆也在 WebView 里做的,就不会有这个问题。

iOS11
iOS11 的 API 可以解决该问题,只要是存在 WKHTTPCookieStore 里的 cookie,WKWebView 每次请求都会携带,存在 NSHTTPCookieStorage 的cookie,并不会每次都携带。于是会发生首次 WKWebView 请求不携带 Cookie 的问题。

解决方法:

在执行 -[WKWebView loadReques:] 前将 NSHTTPCookieStorage 中的内容复制到 WKHTTPCookieStore 中,以此来达到 WKWebView Cookie 注入的目的。示例代码如下:

[self copyNSHTTPCookieStorageToWKHTTPCookieStoreWithCompletionHandler:^{
            NSURL *url = [NSURL URLWithString:@"https://www.v2ex.com"];
            NSURLRequest *request = [NSURLRequest requestWithURL:url];
            [_webView loadRequest:request];
        }];
- (void)copyNSHTTPCookieStorageToWKHTTPCookieStoreWithCompletionHandler:(nullable void (^)())theCompletionHandler; {
    NSArray *cookies = [[NSHTTPCookieStorage sharedHTTPCookieStorage] cookies];
    WKHTTPCookieStore *cookieStroe = self.webView.configuration.websiteDataStore.httpCookieStore;
    if (cookies.count == 0) {
        !theCompletionHandler ?: theCompletionHandler();
        return;
    }
    for (NSHTTPCookie *cookie in cookies) {
        [cookieStroe setCookie:cookie completionHandler:^{
            if ([[cookies lastObject] isEqual:cookie]) {
                !theCompletionHandler ?: theCompletionHandler();
                return;
            }
        }];
    }
}

iOS11之前
通过让所有 WKWebView 共享同一个 WKProcessPool 实例,可以实现多个 WKWebView 之间共享 Cookie(session Cookie and persistent Cookie)数据。不过 WKWebView WKProcessPool 实例在 app 杀进程重启后会被重置,导致 WKProcessPool 中的 Cookie、session Cookie 数据丢失,目前也无法实现 WKProcessPool 实例本地化保存。可以采取 cookie 放入 Header 的方法来做。

WKWebView * webView = [WKWebView new]; 
NSMutableURLRequest * request = [NSMutableURLRequest requestWithURL:[NSURL URLWithString:@"http://xxx.com/login"]]; 
NSString *cookieStr = [NSString stringWithFormat:@"%@=%@",cookie.name,cookie.value];
 [request addValue:cookieStr forHTTPHeaderField:@"Cookie"]; 
 [webView loadRequest:request];

cookieStr的获取可以从本地存储的自定义cookie获取,也可以通过以下方法获取:

HTTPDNSCookieManager.h
#ifndef HTTPDNSCookieManager_h
#define HTTPDNSCookieManager_h
// URL匹配Cookie规则
typedef BOOL (^HTTPDNSCookieFilter)(NSHTTPCookie *, NSURL *);
@interface HTTPDNSCookieManager : NSObject
+ (instancetype)sharedInstance;
/**
 指定URL匹配Cookie策略
 @param filter 匹配器
 */
- (void)setCookieFilter:(HTTPDNSCookieFilter)filter;
/**
 处理HTTP Reponse携带的Cookie并存储
 @param headerFields HTTP Header Fields
 @param URL 根据匹配策略获取查找URL关联的Cookie
 @return 返回添加到存储的Cookie
 */
- (NSArray<NSHTTPCookie *> *)handleHeaderFields:(NSDictionary *)headerFields forURL:(NSURL *)URL;
/**
 匹配本地Cookie存储,获取对应URL的request cookie字符串
 @param URL 根据匹配策略指定查找URL关联的Cookie
 @return 返回对应URL的request Cookie字符串
 */
- (NSString *)getRequestCookieHeaderForURL:(NSURL *)URL;
/**
 删除存储cookie
 @param URL 根据匹配策略查找URL关联的cookie
 @return 返回成功删除cookie数
 */
- (NSInteger)deleteCookieForURL:(NSURL *)URL;
@end
#endif /* HTTPDNSCookieManager_h */
HTTPDNSCookieManager.m
#import <Foundation/Foundation.h>
#import "HTTPDNSCookieManager.h"
@implementation HTTPDNSCookieManager
{
    HTTPDNSCookieFilter cookieFilter;
}
- (instancetype)init {
    if (self = [super init]) {
        /**
            此处设置的Cookie和URL匹配策略比较简单,检查URL.host是否包含Cookie的domain字段
            通过调用setCookieFilter接口设定Cookie匹配策略,
            比如可以设定Cookie的domain字段和URL.host的后缀匹配 | URL是否符合Cookie的path设定
            细节匹配规则可参考RFC 2965 3.3节
         */
        cookieFilter = ^BOOL(NSHTTPCookie *cookie, NSURL *URL) {
            if ([URL.host containsString:cookie.domain]) {
                return YES;
            }
            return NO;
        };
    }
    return self;
}
+ (instancetype)sharedInstance {
    static id singletonInstance = nil;
    static dispatch_once_t onceToken;
    dispatch_once(&onceToken, ^{
        if (!singletonInstance) {
            singletonInstance = [[super allocWithZone:NULL] init];
        }
    });
    return singletonInstance;
}
+ (id)allocWithZone:(struct _NSZone *)zone {
    return [self sharedInstance];
}
- (id)copyWithZone:(struct _NSZone *)zone {
    return self;
}
- (void)setCookieFilter:(HTTPDNSCookieFilter)filter {
    if (filter != nil) {
        cookieFilter = filter;
    }
}
- (NSArray<NSHTTPCookie *> *)handleHeaderFields:(NSDictionary *)headerFields forURL:(NSURL *)URL {
    NSArray *cookieArray = [NSHTTPCookie cookiesWithResponseHeaderFields:headerFields forURL:URL];
    if (cookieArray != nil) {
        NSHTTPCookieStorage *cookieStorage = [NSHTTPCookieStorage sharedHTTPCookieStorage];
        for (NSHTTPCookie *cookie in cookieArray) {
            if (cookieFilter(cookie, URL)) {
                NSLog(@"Add a cookie: %@", cookie);
                [cookieStorage setCookie:cookie];
            }
        }
    }
    return cookieArray;
}
- (NSString *)getRequestCookieHeaderForURL:(NSURL *)URL {
    NSArray *cookieArray = [self searchAppropriateCookies:URL];
    if (cookieArray != nil && cookieArray.count > 0) {
        NSDictionary *cookieDic = [NSHTTPCookie requestHeaderFieldsWithCookies:cookieArray];
        if ([cookieDic objectForKey:@"Cookie"]) {
            return cookieDic[@"Cookie"];
        }
    }
    return nil;
}
- (NSArray *)searchAppropriateCookies:(NSURL *)URL {
    NSMutableArray *cookieArray = [NSMutableArray array];
    NSHTTPCookieStorage *cookieStorage = [NSHTTPCookieStorage sharedHTTPCookieStorage];
    for (NSHTTPCookie *cookie in [cookieStorage cookies]) {
        if (cookieFilter(cookie, URL)) {
            NSLog(@"Search an appropriate cookie: %@", cookie);
            [cookieArray addObject:cookie];
        }
    }
    return cookieArray;
}
- (NSInteger)deleteCookieForURL:(NSURL *)URL {
    int delCount = 0;
    NSHTTPCookieStorage *cookieStorage = [NSHTTPCookieStorage sharedHTTPCookieStorage];
    for (NSHTTPCookie *cookie in [cookieStorage cookies]) {
        if (cookieFilter(cookie, URL)) {
            NSLog(@"Delete a cookie: %@", cookie);
            [cookieStorage deleteCookie:cookie];
            delCount++;
        }
    }
    return delCount;
}
@end

使用方法示例:

WKWebView * webView = [WKWebView new]; 
 NSMutableURLRequest * request = [NSMutableURLRequest requestWithURL:[NSURL URLWithString:@"http://xxx.com/login"]]; 
NSString *value = [[HTTPDNSCookieManager sharedInstance] getRequestCookieHeaderForURL:url];
[request setValue:value forHTTPHeaderField:@"Cookie"];
 [webView loadRequest:request];

文中有错误的地方,还望大家包涵并指正,谢谢.

参考链接
iOS 关于token、cookie的那些事
详解iOS App开发中Cookie的管理方法
iOS WKWebView 与 UIWebView Cookie机制的同步
iOS WebView 中的 Cookie 处理业务场景“IP直连”方案说明

©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 211,948评论 6 492
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 90,371评论 3 385
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 157,490评论 0 348
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 56,521评论 1 284
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 65,627评论 6 386
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 49,842评论 1 290
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 38,997评论 3 408
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 37,741评论 0 268
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 44,203评论 1 303
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 36,534评论 2 327
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 38,673评论 1 341
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 34,339评论 4 330
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 39,955评论 3 313
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 30,770评论 0 21
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 32,000评论 1 266
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 46,394评论 2 360
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 43,562评论 2 349

推荐阅读更多精彩内容