从壹开始 [ Id4 ] 之二║ 基础知识集合 & 项目搭建一

前言

哈喽大家又见面啦,感觉好久没更新了,这几天看了一本书《解忧杂货铺》,嗯挺好的,推荐一下😀。

不过还是要学习了,这些天简单的看了看 Id4 的资料,才发现原来关于 Id4 的系列文章真是数不胜数,而且还有很多的深度好文章,特别是 Dave 的视频教程,说的灰常之详细,所以一度打消了我写这一系列的冲动和信心,不过还是有一部分的小伙伴还没有学习过,所以我决定我以后就把 IdentityServer4 的基础概念知识点,就少写些,重点写如何在项目中使用吧,本文的知识点,简单过一遍,脑子里有这个东西就行了,再以后的开发中,多动手就知道了,特别简单的,这里说明一下,在以前的Blog.Core 系列中,有关 JWT 的知识,我单拎出来一个小Demo,有不懂的可以下载看看(https://github.com/anjoy8/Blog.Core/blob/master/Blog.Core/wwwroot/Autho.jwt.rar)。

零、今天要完成红色的部分

image

一、常见术语

image

1、身份认证服务器(IdentityServer)

IdentityServer 是基于OpenID Connect协议标准的身份认证和授权程序,它实现了OpenID Connect 和 OAuth 2.0 协议。

同样的角色,不同的文档使用不同的术语。在有些文档中,它(IdentityServer)可能会被叫做安全令牌服务器(security token service)、身份提供者(identity provider)、授权服务器(authorization server)、 标识提供方((IP-STS,什么是IP-STS)等等。

但是它们都是一样的,都是向客户端发送安全令牌(security token),

IdentityServer有许多功能:

  • 保护你的资源
  • 使用本地帐户或通过外部身份提供程序对用户进行身份验证
  • 提供会话管理和单点登录
  • 管理和验证客户机
  • 向客户发出标识和访问令牌
  • 验证令牌

2、用户(User)

用户是使用已注册的客户端(指在id4中已经注册)访问资源的人。

3、客户端(Client)

客户端就是从identityserver请求令牌的软件(你可以理解为一个app即可),既可以通过身份认证令牌来验证识别用户身份,又可以通过授权令牌来访问服务端的资源。但是客户端首先必须在申请令牌前已经在identityserver服务中注册过。

实际客户端不仅可以是Web应用程序,app或桌面应用程序(你就理解为pc端的软件即可),SPA,服务器进程等。

4、资源(Resources)

资源就是你想用identityserver保护的东东,可以是用户的身份数据或者api资源。
每一个资源都有一个唯一的名称,客户端使用这个唯一的名称来确定想访问哪一个资源(在访问之前,实际identityserver服务端已经配置好了哪个客户端可以访问哪个资源,所以你不必理解为客户端只要指定名称他们就可以随便访问任何一个资源)。

用户的身份信息实际由一组claim组成,例如姓名或者邮件都会包含在身份信息中(将来通过identityserver校验后都会返回给被调用的客户端)。

API资源就是客户端想要调用的功能(通常以json或xml的格式返回给客户端,例如webapi,wcf,webservice),通常通过webapi来建立模型,但是不一定是webapi,我刚才已经强调可以使其他类型的格式,这个要看具体的使用场景了。

5、身份令牌(Identity Token)

身份令牌表示身份验证过程的结果。 它最低限度地标识了某个用户,还包含了用户的认证时间和认证方式。 它可以包含额外身份数据。

一个身份令牌指的就是对认证过程的描述。它至少要标识某个用户(Called the sub aka subject claim)的主身份信息,和该用户的认证时间和认证方式。但是身份令牌可以包含额外的身份数据,具体开发者可以自行设定,但是一般情况为了确保数据传输的效率,开发者一般不做过多额外的设置,大家也可以根据使用场景自行决定。

6、访问令牌(Access Token)

访问令牌允许访问API资源。 客户端请求访问令牌并将其转发到API。 访问令牌包含有关客户端和用户的信息(如果存在)。 API使用该信息来授权访问其数据。

访问令牌允许客户端访问某个 API 资源。客户端请求到访问令牌,然后使用这个令牌来访问 API资源。访问令牌包含了客户端和用户(如果有的话,这取决于业务是否需要,但通常不必要)的相关信息,API通过这些令牌信息来授予客户端的数据访问权限。

7、刷新令牌(Refresh Token)

access token 是客户端访问资源服务器的令牌。拥有这个令牌代表着得到用户的授权。然而,这个授权应该是临时的,有一定有效期。这是因为,access token 在使用的过程中可能会泄露。给 access token 限定一个较短的有效期可以降低因 access token 泄露而带来的风险。

然而引入了有效期之后,客户端使用起来就不那么方便了。每当 access token 过期,客户端就必须重新向用户索要授权。这样用户可能每隔几天,甚至每天都需要进行授权操作。这是一件非常影响用户体验的事情。希望有一种方法,可以避免这种情况。

于是 Oauth2.0 引入了 refresh token 机制。refresh token 的作用是用来刷新 access token。鉴权服务器提供一个刷新接口,例如:

http://xxx.xxx.com/refresh?refreshtoken=&client_id=

传入 refresh token 和 client_id,鉴权服务器验证通过后,返回一个新的 access token。为了安全,Oauth2.0 引入了两个措施:

1,Oauth2.0 要求,refresh token 一定是保存在客户端的服务器上的,而绝不能存放在狭义的客户端(例如移动 app、PC端软件) 上。调用 refresh 接口的时候,一定是从服务器到服务器的访问;

2,Oauth2.0 引入了 client_secret 机制。即每一个 client_id 都对应一个 client_secret。这个 client_secret 会在客户端申请 client_id 时,随 client_id 一起分配给客户端。客户端必须把 client_secret 妥善保管在服务器上,决不能泄露。刷新 access token 时,需要验证这个 client_secret。

于是,实际上的刷新接口应该是类似这样的:

http://xxx.xxx.com/refresh?refreshtoken=&client_id=&client_secret=

以上就是 refresh token 机制。refresh token 的有效期非常长,会在用户授权时,随 access token 一起重定向到回调 url,传递给客户端。

二、 OAuth 2.0 和 OpenID Connect

image

1、什么是OAuth2.0 ?

OAuth(开放授权)是一个开放标准,允许用户让第三方应用访问该用户在某一网站上存储的私密的资源(如照片,视频,联系人列表),而无需将用户名和密码提供给第三方应用。就比如我用QQ登录博客园,那博客园(第三方应用)的昵称就可以是我的QQ(某网站)昵称,它获取到了我的QQ昵称,并存到了博客园的数据库,我以后就一直可以使用QQ来登录博客园,但是博客园却不知道我QQ的用户名和密码。

OAuth 允许用户提供一个令牌,而不是用户名和密码来访问他们存放在特定服务提供者的数据。每一个令牌授权一个特定的网站(例如,视频编辑网站)在特定的时段(例如,接下来的 2 小时内)内访问特定的资源(例如仅仅是某一相册中的视频)。这样,OAuth 让用户可以授权第三方网站访问他们存储在另外服务提供者的某些特定信息,而非所有内容。

OAuth 2.0 四种授权模式:

授权码模式(authorization code)

简化模式(implicit grant type)

密码模式(resource owner password credentials)

客户端模式(Client Credentials Grant)

具体的概念和如何使用不细说,以后会分四篇文章详细说明,今天主要是基础知识和项目的搭建。

2、什么是 OpenID Connect ?

简单来说:

  • OpenID Connect是基于OAuth 2.0规范族的可互操作的身份验证协议。它使用简单的REST / JSON消息流来实现,和之前任何一种身份认证协议相比,开发者可以轻松集成。
  • OpenID Connect允许开发者验证跨网站和应用的用户,而无需拥有和管理密码文件。
  • OpenID Connect允许所有类型的客户,包括基于浏览器的JavaScript和本机移动应用程序,启动登录流动和接收可验证断言对登录用户的身份。

进一步来说:

  • OpenID Connect是OAuth 2.0协议之上的简单身份层,用 API 进行身份交互的框架,允许客户端根据授权服务器的认证结果最终确认用户的身份,以及获取基本的用户信息
  • 它支持包括Web、移动、JavaScript在内的所有客户端类型;
  • 它是可扩展的协议,允许你使用某些可选功能,如身份数据加密、OpenID提供商发现、会话管理;

OpenID Connect vs OpenID 2.0:OpenID Connect完成很多与OpenID 2.0相同的任务,是API-friendly,定义了可选的签名和加密的机制;OAuth 1.0和OpenID 2.0的集成需要扩展,而OpenID Connect协议本身就建立在OAuth 2.0之上。

为什么开发者要使用OpenID Connect?
因为它很简单,可靠,安全,并让他们摆脱困难和危险的存储和管理别人的密码。也有好处,它让用户的生活更容易在网站注册和注册从而减少遗弃。

3、JWT 和 OAuth2.0 的比较

要比较JWT和OAuth2,首先要明白一点就是,这两个根本没有可比性,是两个完全不同的东西。

  • JWT是一种认证协议
    JWT提供了一种用于发布接入令牌(Access Token),并对发布的签名接入令牌进行验证的方法。 令牌(Token)本身包含了一系列声明,应用程序可以根据这些声明限制用户对资源的访问。

  • OAuth2是一种授权框架
    另一方面,OAuth2是一种授权框架,提供了一套详细的授权机制(指导)。用户或应用可以通过公开的或私有的设置,授权第三方应用访问特定资源

既然 JWT和OAuth2.0 没有可比性,为什么还要把这两个放在一起说呢?实际中确实会有很多人拿 JWT和OAuth2.0 作比较。很多情况下,在讨论OAuth2的实现时,会把JSON Web Token作为一种认证机制使用。这也是为什么他们会经常一起出现。

4、OpenID 是怎么认证用户的?

一个网站如果想要接入 OpenID 认证是非常简单的,不需要创建应用,不需要 App Key ,不需要 Secret ,只需要将用户导向 OpenID Provider 的 Entry 并带上 Callback ,用户只要同意提供信息,你就可以拿到这个用户的“唯一标识”。

请注意这里我使用了“唯一标识”这种说法,因为对于网站来说,OpenID Provider 提供的既不是用户的 UID ,也不是用户的 E-Mail ,比如 Google 在默认情况下提供的就是一个几十位长的字符串,这个字符串是随机生成的,第三方网站无法从中获得用户的任何私人信息。这么说可能很抽象,举个例子:

比如我用 Google 的 OpenID 服务登录 xxx.comxxx.com 先把我导向 Google 的授权页面,我使用 Google 帐号 test@gmail.com 登录并同意后,页面跳回 xxx.comxxx.com 拿到了我的“唯一标识”,这个唯一标识可能是 cdxxxxxxxaaf6b73bcb04a1 ,xxx.com 从这个字符串里无法获得任何 test@gmail.com 的个人信息(甚至连邮箱地址也不知道), xxx.com 只知道以后只要使用谷歌登录并返回 cdxxxxxxxaaf6b73bcb04a1 这个标识符,那就是我在登录。

显而易见,OpenID 是专为登录认证而生,它使用简单,门槛很低,但是如果你想在认证过程中获得用户的其他信息(比如 E-Mail )就得多做一步了。

你这个时候可能就会问了,那有哪些网站使用的是 OpenID 登录的呢,我简单看了看,好像都是基于 OAuth 开发的,而且也看到了一个博问,是这么说的:

image

具体的咱们就不分析了,现在基本都是使用的 OAuth2.0 ,不过现在使用 Id4 是结合了 OAuth2.0 和 OpenID的。

5、OAuth 又是怎么认证用户的?

与 OpenID 相比,网站想接入 OAuth 要稍微麻烦点,网站需要先创建应用,拿到 Key 和 Secret ,才能接入 Provider 。

OAuth 的授权过程并不是身份认证的过程,这一点需要特别清楚,网站走完 OAuth 流程并拿到用户的授权 token 后还需要通过 token 调用相应的用户信息接口才能获得“唯一标识”,举个例子:

我想通过新浪微博登录 xxx.com ,xxx.com 要先把我 redirect 到新浪微博的授权页面,我通过微博帐号登录并授权后,页面跳回 xxx.com ,xxx.com 拿到我的访问 token 后还要再调用一个接口来获得我的新浪会员 UID ,这个 UID 就是新浪用户的“唯一标识”了。

可以看出,OAuth 相对于 OpenID 最大的区别就是,网站实际上是拿到了你的帐户访问权限继而确认你的身份,这是一个安全隐患,因为网站在拿到你的“唯一标识”的同时还拿到了一把你的账户的 “临时钥匙”。至于网站会不会拿这把钥匙“干坏事”,这个只有站长心里清楚。同时 OAuth 还比 OpenID 多了几个额外的请求步骤,登录所费时间一定是长于 OpenID 的。

6、OAuth和OpenID的区别

OAuth关注的是authorization;而OpenID侧重的是authentication。从表面上看,这两个英文单词很容易混淆,但实际上,它们的含义有本质的区别:

  • authorization: n. 授权,认可;批准,委任
  • authentication: n. 证明;鉴定;证实

OAuth关注的是授权,即:“用户能做什么”;而OpenID关注的是证明,即:“用户是谁”。

OpenID是关于证明、证实身份(Authentication)的,好比火车站进站的时候拿出身份证和车票来看,比对是否同一个人。这是在回答「我是谁?这就是我」,是为了证实「这不是一个匿名的不可查的信息源头」,因为匿名对象和信息对网络服务商来说不好统计管理,也不利于产生价值。

OAuth 是关于授权、许可(Authorization)的,好比坐飞机过安检的时候除了看身份证,还要求掏出兜里的东西,拿出包里的东西、手机等随身物品以便检查,这时就需要得到被检查人的许可才行,被检查人有权利扭头就走,但要想登机,必须给予许可、掏出物品。这是在回答「我同意让你对我进一步做些什么」,是为了在被授予权限的前提下,更多的获取除了账号密码以外的个人信息,例如:联系人通讯录,邮箱号,电话号,地址,照片,聊天记录,网络发言、活动记录,GPS 信息等等,来满足用户对服务的功能需要,或者「其他需要」。

<pre style="color: rgb(0, 0, 0); font-family: "Courier New"; font-size: 12px; margin: 5px 8px; padding: 5px;">//这里有个网友的说明,我感觉挺好的</pre>

如今越来越多的网站,以及一些应用程序都开始使用第三方社交平台账户登录,那这里就会涉及到安全性的问题,隐私的问题,你不能随意来获取我的资料,当然你来使用我的资料,你要经过用户的同意,那这个用户是不是我平台上,还是要来向我求证,那在这个过程中,实际上就出现了两个过程,我们还是直接使用上次的例子来说明,比较直观,CSDN使用QQ登录,进入csdn的登录页,点击使用QQ登录:

image
   在进入到QQ登录界面后,最开始是要请求认证,用户输入QQ号和密码,点击登录,腾讯互联会先进行验证该用户是否为我的用户,如果是我的用户,那么我会通知你(CSDN),他是我的用户,你可以使用该账户登录你的系统,这个过程就是认证(Authentication),认证就是证明你是谁,你是否是真实存在的,就好像,快递员来给你送快递,让你出示你的身份证,他确定你是本人后,把快递给你,这就是OpenID。

  而在QQ授权登录下方,有两给CheckBox复选框,可以允许CSDN获得您的昵称、头像、性别,这是在认证之后的事了,在腾讯互联你是我平台的用户后,你可以自己选择CSDN是否有权去获取你的相关信息,当你勾选后,腾讯互联就把你的这些基本信息给了CSDN,这个过程就是授权(Authorization),授权就是确定了你是谁后,又把属于你的东西给了别人,犹如你向快递员出示了身份证,然后你又把你房门的密码给了他,并告诉他说,我把房门密码给你,你帮我放到我客厅里吧。

7、OAuth 、OpenID 和 OpenID Connect 的关系

简要而言,OIDC是一种安全机制,用于应用连接到身份认证服务器(Identity Service)获取用户信息,并将这些信息以安全可靠的方法返回给应用。

在最初,因为OpenID1/2经常和OAuth协议(一种授权协议)一起提及,所以二者经常被搞混。

  • OpenIDAuthentication,即认证,对用户的身份进行认证,判断其身份是否有效,也就是让网站知道“你是你所声称的那个用户”;

  • OAuthAuthorization,即授权,在已知用户身份合法的情况下,经用户授权来允许某些操作,也就是让网站知道“你能被允许做那些事情”。
    由此可知,授权要在认证之后进行,只有确定用户身份只有才能授权。

(身份验证)+ OAuth 2.0 = OpenID Connect

OpenID Connect是“认证”和“授权”的结合,因为其基于OAuth协议,所以OpenID-Connect协议中也包含了client_idclient_secret还有redirect_uri等字段标识。这些信息被保存在“身份认证服务器”,以确保特定的客户端收到的信息只来自于合法的应用平台。这样做是目的是为了防止client_id泄露而造成的恶意网站发起的OIDC流程。

举个例子。某个用户使用Facebook应用“What online quiz best describes you?” ,该应用可以通过Facebook账号登录,则你可以在应用中发起请求到“身份认证服务器”(也就是Facebook的服务器)请求登录。这时你会看到如下界面,询问是否授权。

在 OAuth 中,这些授权被称为scope。OpenID-Connect也有自己特殊的scope--openid ,它必须在第一次请求“身份鉴别服务器”(Identity Provider,简称IDP)时发送过去。

三、创建基于EFCore的Idp项目

image

你可以参考官方文档:Docs » Welcome to IdentityServer4

1、建立空的NetCore MVC 项目

这个过程很简单,因为你能看这个系列,证明你的 NetCore 功底已经很高了,至少中等,如果不会,请时光机回到我的第一个前后端分离系列。

1、创建一个新的ASP.NET Core项目;//可以使用命令行创建

2、选择“空白”选项;//这里创建空的应用程序是为了后面方便引入UI界面

3、设置项目使用 kestrel 服务器,删除IIS;

4、配置端口号;//我这里是5002,可自定义设置

image

2、安装并配置IdentityServer4

1、使用 nuget 添加 IdentityServer4 包;//当然你也可以使用命令: Install-Package IdentityServer4

2、配置认证服务中间件管道;//app.UseIdentityServer();

3、配置 IdentityServer 相关服务;

这里不仅要把 IdentityServer 注册到容器中, 还要至少对其配置四点内容:

1、这个 Authorization Server 保护了哪些 API (资源);

2、哪些客户端 Client(应用) 可以使用这个 Authorization Server;

3、指定可以使用 Authorization Server 授权的 Users(用户);

4、指定作用域定义系统中的 IdentityResources 资源;//这个在以后说到 OpenId的时候再说,这里伏笔

首先在项目根目录下,建立文件 InMemoryConfig.cs:

<pre style="color: rgb(0, 0, 0); font-family: "Courier New"; font-size: 12px; margin: 5px 8px; padding: 5px;"> public static class InMemoryConfig
{ // 这个 Authorization Server 保护了哪些 API (资源)
public static IEnumerable<ApiResource> GetApiResources()
{ return new[]
{ new ApiResource("blog.core.api", "Blog.Core API")
};
} // 哪些客户端 Client(应用) 可以使用这个 Authorization Server
public static IEnumerable<Client> GetClients()
{ return new[]
{ new Client
{
ClientId = "blogvuejs",//定义客户端 Id
ClientSecrets = new [] { new Secret("secret".Sha256()) },//Client用来获取token
AllowedGrantTypes = GrantTypes.ResourceOwnerPasswordAndClientCredentials,//这里使用的是通过用户名密码和ClientCredentials来换取token的方式. ClientCredentials允许Client只使用ClientSecrets来获取token. 这比较适合那种没有用户参与的api动作
AllowedScopes = new [] { "blog.core.api" }// 允许访问的 API 资源
}
};
} // 指定可以使用 Authorization Server 授权的 Users(用户)
public static IEnumerable<TestUser> Users()
{ return new[]
{ new TestUser
{
SubjectId = "1",
Username = "laozhang",
Password = "laozhang" }
};
}
}</pre>

GetApiResources:这里指定了name和display name, 以后api使用authorization server的时候, 这个name一定要一致, 否则就不好用的。

GetClients: Client的属性太多了,基本的上边已经注释了

Users: 这里的内存用户的类型是TestUser, 只适合学习和测试使用, 实际生产环境中还是需要使用数据库来存储用户信息的, 例如接下来会使用asp.net core identity. TestUser的SubjectId是唯一标识.

image

然后回到 Startup 的 ConfigureServices 方法种:

我们需要对token进行签名, 这意味着 identity server 需要一对public和private key。不过我们现在是本地调试,可以告诉identity server在程序的运行时候对这项工作进行设定: AddDeveloperSigningCredential(),它默认会存到硬盘上的, 所以每次重启服务不会破坏开发时的数据同步。这个方法只适合用于identity server4在单个机器运行, 如果是 production 你得使用AddSigningCredential()这个方法:

<pre style="color: rgb(0, 0, 0); font-family: "Courier New"; font-size: 12px; margin: 5px 8px; padding: 5px;"> public class Startup
{ public IConfiguration Configuration { get; } public IHostingEnvironment Environment { get; } public Startup(IConfiguration configuration, IHostingEnvironment environment)
{
Configuration = configuration;
Environment = environment;
}
public void ConfigureServices(IServiceCollection services)
{
services.AddMvc(); var builder = services.AddIdentityServer(options => {
options.Events.RaiseErrorEvents = true;
options.Events.RaiseInformationEvents = true;
options.Events.RaiseFailureEvents = true;
options.Events.RaiseSuccessEvents = true;
}) // in-memory, code config
.AddTestUsers(InMemoryConfig.Users().ToList())
.AddInMemoryApiResources(InMemoryConfig.GetApiResources())
.AddInMemoryClients(InMemoryConfig.GetClients());

        builder.AddDeveloperSigningCredential(); if (Environment.IsDevelopment())
        {
            builder.AddDeveloperSigningCredential();
        } else { throw new Exception("need to configure key material");
        }

        services.AddAuthentication();//配置认证服务

    } // This method gets called by the runtime. Use this method to configure the HTTP request pipeline.
    public void Configure(IApplicationBuilder app, IHostingEnvironment env)
    { if (env.IsDevelopment())
        {
            app.UseDeveloperExceptionPage();
        } app.UseIdentityServer();//启动 IdentityServer
        app.UseMvcWithDefaultRoute();//启动 MVC
    }
}</pre>

然后运行一下,在 Postman 中,尝试通过上边的用户名密码来获取 Token :

image

是不是很熟悉,如果你用过 NetCore 的授权验证的话,这些肯定都能看懂。

这里你一定很好奇,我是怎么知道要使用这个地址 localhost:5002/connect/token 来获取Token的? ,一共还有多少个接口地址呢,要是有一个界面就好了。别着急往下看。IdentityServer4 官方已经考虑到了。

3、关于QuickStart UI

官方为我们提供了一个快速启动的UI界面,我们只需要下载下来即可,这里有两个方法:

1、直接从这个地址下来下载,拷贝到项目中,一共三个文件夹;// https://github.com/IdentityServer/IdentityServer4.Quickstart.UI

2、在当前文件夹中执行命令,自动下载;

iex ((New-Object System.Net.WebClient).DownloadString('https://raw.githubusercontent.com/IdentityServer/IdentityServer4.Quickstart.UI/master/getmaster.ps1'))
image

这样我们的项目界面已经成功的获取到了,除此之外,记得要配置中间件来使用静态文件:

<pre style="color: rgb(0, 0, 0); font-family: "Courier New"; font-size: 12px; margin: 5px 8px; padding: 5px;">app.UseStaticFiles();</pre>

4、运行展示

配置好后,F5运行,就可以看到了界面,而且也看到了我们上边说到的 token 获取的接口地址 localhost:5002/connect/token:

image

5、配置 EFCore 连接到数据库

因为篇幅的问题,这个下次再详细说明,不过我的 GitHub 上已经写好了,有需要的可以看看。

四、结语

今天咱们主要通过一个小栗子,把我们的IdentityServer授权服务器搭建好了,并且做了相应配置,也获取到了 Token ,是不是很简单?感觉 Id4 也没有我们想象的那么难是吧,关键就是上边的Config.cs 中的三个配置,总结来说:

有哪些用户可以通过哪些客户端来访问我们的哪些API保护资源。

这要我们这些配置好了,自然我们也就可以使用 Id4 了,当然这个仅仅是获取到了 Token,那如何连数据库,如何应用在 API 资源服务器,又如何在客户端配置呢,咱们下次再见咯。

五、Github

https://github.com/anjoy8/Blog.IdentityServer

-- END ---

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

推荐阅读更多精彩内容

  • 以下是官网直译:https://oauth.net/ 1. 首页 OAuth是一种开放协议(注:协议是公开的,任何...
    KennethChen93阅读 11,180评论 0 20
  • OAuth是一个关于授权(authorization)的开放网络标准,在全世界得到广泛应用,目前的版本是2.0版。...
    薛定谔的猫_1406阅读 1,886评论 0 1
  • 1. 引言 现在的应用开发层出不穷,基于浏览器的网页应用,基于微信的公众号、小程序,基于IOS、Android的A...
    圣杰阅读 36,276评论 7 48
  • 随着流逝的时间带动,总有些人会忘记自己的初心,久而久之最初来到这片净土的灵魂也随之开始变浑浊。他们迷失了…...
    不语寒夜阅读 191评论 0 0
  • 玉牖虚开残月小,帘外三更,帘内灯光杳。何处寒蛩鸣露草,无端总是凄凉调。 满地落花慵不扫,案上新词,面上春风恼。酒尽...
    君怀璧阅读 361评论 22 37