• IdentityServer3 v1文档


    IdentityServer3

    目录

    目录 1

    第一章 IdentityServer3 1.x 5

    v 超链接 5

    Ø 概述 5

    Ø 配置 5

    Ø 端点 5

    Ø 先进的 5

    Ø 实体框架支持客户、范围和操作数据 6

    Ø ws - federation 6

    Ø 资源 6

    第二章 概述 6

    v 大局 6

    v 术语 7

    v OpenID提供者连接(凤凰社) 8

    v 客户端 8

    v 用户 8

    v 范围 8

    Ø 身份范围 8

    Ø 资源范围 9

    第三章 认证/令牌请求 9

    v 身份标识 9

    v 访问令牌 9

    v 特性和规格 9

    v 包装和构建 9

    Ø 核心 9

    Ø 配置商店 9

    Ø 用户存储 9

    Ø 插件 9

    Ø 访问令牌验证中间件 10

    v 开发构建 10

    Ø 创建最简单的OAuth2授权服务器,客户端和API 10

    Ø 设置IdentityServer 10

    Ø 注册的API 10

    Ø 注册客户端 10

    Ø 配置IdentityServer 11

    Ø 托管IdentityServer 12

    v 添加一个API 12

    Ø 创建网络主机 12

    Ø 添加一个控制器 12

    Ø 添加启动 12

    v 添加一个控制台客户 13

    v 添加一个用户 13

    Ø 添加一个用户服务 13

    Ø 添加一个客户 14

    Ø 更新的API 15

    Ø 更新客户端 15

    v 下一步要做什么 16

    v 第1部分- MVC身份验证和授权 16

    Ø 创建web应用程序 16

    Ø 添加IdentityServer 17

    Ø 配置IdentityServer -客户 17

    Ø 配置IdentityServer——用户 18

    Ø 添加启动 18

    Ø RAMMFAR 19

    v 添加和配置OpenID身份验证连接中间件 19

    v 添加一个受保护的资源和Claims 19

    Ø 身份验证和Claims 20

    Ø 添加角色和范围 20

    Ø 改变了中间件配置要求的角色 21

    Ø 要求转换 21

    Ø 授权 23

    Ø 资源授权 23

    Ø 角色权限 24

    Ø 更多的授权和处理拒绝访问场景 24

    Ø 添加注销 25

    Ø 添加谷歌身份验证 26

    v 第2部分-添加和调用Web API 27

    Ø 添加Web API项目 27

    Ø 添加一个测试控制器 28

    Ø 连接在启动Web API和安全 28

    Ø 注册在IdentityServer API 29

    Ø 注册一个Web API客户端 30

    Ø 调用API 30

    Ø 代表用户调用的API 32

    第四章 配置 33

    v 概述 33

    v IdentityServer选项 34

    v 服务工厂 34

    v 强制性的 35

    v 强制性的生产场景(但默认内存中实现) 35

    v 可选(可以更换,但有默认的实现) 35

    v 内存中的工厂 36

    第五章 客户 36

    Ø 例如:为隐式流配置客户端 37

    Ø 例如:为资源所有者流配置客户端 38

    v 范围和Claims 39

    Ø Thinktecture.IdentityServer.Core.Models.Scope类的模型OpenID连接或OAuth2范围。 39

    v 身份验证选项 40

    v 身份提供者 41

    v 添加ws - federation身份提供者 42

    v HSTS 43

    v CORS 43

    v 歌珥政策服务 43

    Ø 提供实现 43

    v 弃用:CorsPolicy 43

    Ø CorsPolicy 43

    v 日志记录 44

    v 配置诊断 44

    v 配置系统 诊断提供者 44

    v 配置TraceSource提供者 45

    v 插装自己的代码 45

    v 事件 45

    v 配置事件 45

    第六章 端点 46

    v 授权/身份验证端点 46

    Ø 支持参数 46

    Ø 例子 46

    v 令牌端点 47

    Ø 支持参数 47

    Ø 身份验证 47

    Ø 例子 47

    v 用户信息端点 48

    Ø 例子 48

    v 发现端点 48

    Ø 例子 48

    v 注销端点 48

    v 令牌撤销 48

    v 访问令牌验证端点 49

    Ø 例子 49

    v 身份令牌验证端点 49

    Ø 例子 49

    v CSP端点 49

    第七章 先进的 50

    v 刷新令牌 50

    Ø 设置在客户端类 50

    Ø 使用 50

    Ø 样品 50

    v 定制服务 50

    v 强制性的服务 50

    v 注册定制服务 50

    Ø 服务清理 51

    第八章 依赖注入 51

    v 注入IdentityServer服务 51

    v 注入定制服务 52

    Ø 没有一个接口定制服务 52

    v 创建自定义 53

    Ø 获得其他依赖项 53

    Ø 叫依赖 53

    v 实例化与登记模式 53

    v 客户端缓存结果、范围和用户存储 54

    v 默认的缓存 54

    v 自定义缓存 54

    v 自定义视图 54

    Ø 默认视图服务 54

    Ø 自定义视图服务 55

    v 本地化的消息 56

    v CSP 56

    Ø CspOptions 56

    v 用户服务 56

    v 身份验证 57

    Ø 验证结果 57

    第九章 部署 58

    v 数据保护 58

    v 终止SSL 58

    v 签名密钥 59

    v 配置数据 59

    v 操作数据 59

    v 缓存 59

    第十章 实体框架支持客户、范围和操作数据 59

    v 概述 59

    v 配置客户机和范围 59

    v 操作数据 59

    v 客户和范围 59

    v 商店 59

    Ø ClientStore 59

    Ø ScopeStore 59

    v 登记 59

    第十一章 操作数据 60

    v 数据清理 60

    第十二章 模式变化和迁移 60

    v dbcontext 60

    v 使迁移 60

    第十三章 ws - federation 61

    v 添加ws - federation支持 61

    v 定义依赖方 62

    v 端点 63

    v 登录/出 63

    v 元数据 63

    第十四章 资源 63

    v 资源 63

    Ø 规范 64

    Ø 文章 64

    Ø 视频 64

    Ø 培训 64

    v 社区插件 64

    第十五章 中间件为外部认证 64

    v 微软的武士刀 64

    v SAML2p 64

    v 其他社区的贡献 65

     

    第一章IdentityServer3 1.x

    v 超链接

    Ø 概述

    大局

    术语

    特性和规格

    包装

    开始:创建最简单的OAuth2授权服务器,客户端和API

    开始:MVC认证& Web api

    Ø 配置

    概述

    选项

    服务工厂

    内存中的工厂

    客户

    范围和Claims

    身份验证选项

    身份提供者

    hst

    歌珥

    日志记录

    事件

    Ø 端点

    授权/身份验证

    令牌

    用户信息

    发现

    注销

    令牌撤销

    访问令牌验证

    身份令牌验证

    CSP错误报告

    Ø 先进的

    刷新令牌

    注册服务

    DI的服务

    客户端缓存,范围和用户存储

    自定义视图

    本地化的消息

    CSP

    用户服务

    部署

    Ø 实体框架支持客户、范围和操作数据

    概述

    客户和范围

    操作数据

    模式变化和迁移

    Ø ws - federation

    添加支持ws - federation

    定义依赖方

    端点

    Ø 资源

    概述

    社区插件

    中间件为外部认证

     

    第二章概述

    v 大局

    大多数现代应用程序看起来或多或少是这样的:

    典型的交互: 

    l 浏览器与web应用程序通信 

    l Web应用程序与Web api(有时自己,有时代表一个用户) 

    l 基于浏览器的应用程序与web api通信 

    l 本机应用程序与web api通信 

    l 基于服务器的应用程序与web api通信  

    l Web apiWeb api(有时自己,有时代表一个用户)

    通常每一层(中间层和后端,前端)和保护资源 实现身份验证和/或授权,通常针对同一用户存储。

    这就是为什么我们没有实现这些基本安全功能的业务应用程序/端点本身而是关键功能的外包服务,安全令牌服务。

    这将导致以下安全架构和使用协议:

    这将安全问题划分为两个部分。

    u 身份验证

    身份验证是必要的,当一个应用程序需要知道当前用户的身份。 通常这些应用程序代表用户管理数据,需要确保用户只能 他被允许访问数据。 最常见的例子(经典)web应用程序 但本机和JS-based应用程序也需要进行身份验证。

    最常见的身份验证协议SAML2p,ws - federation,OpenID连接——SAML2p 最受欢迎和最广泛的部署。

    OpenID连接是最新的三个,但一般认为是,因为它有未来 现代应用最有潜力的产业。 它从一开始就为移动应用程序场景 和被设计成API友好。

    u API访问

    应用程序有两个基本方法,它们与api使用应用程序标识或授权用户的身份。 有时两方面需要的总和。

    OAuth2是一种协议,它允许应用程序请求访问令牌从安全令牌服务和使用它们 与api。 这降低了复杂性在客户机应用程序的api 可以集中的身份验证和授权。

    OpenIDOAuth2——更好的联系在一起

    OpenID连接和OAuth2非常相似——事实上OpenID是一个连接扩展OAuth2之上。 这意味着您可以将两个基本安全问题-认证和API访问到一个单独的协议 通常一个往返安全令牌服务。

    这就是为什么我们相信OpenID的组合连接和安全的现代OAuth2是最好的方法 应用程序在可预见的未来。 IdentityServer3是这两个协议的一个实现 高度优化的解决今天的移动的典型安全问题,本机和web应用程序。

    v 术语

    规范、文档对象模型使用一个特定的术语,你应该知道的。

    v OpenID提供者连接(凤凰社)

    IdentityServer OpenID提供者连接,它实现了OpenID连接协议(以及OAuth2)

    不同的文献使用不同的条款相同的角色,你也可能找到安全令牌服务身份提供者,授权服务器,IP-STS等等。

    但总而言之都是一样的:一个软件问题给客户的安全令牌。

    IdentityServer有很多工作和功能,包括: 

    l 验证用户使用本地帐户存储或通过外部身份提供者 

    l 提供会话管理和单点登录 

    l 管理和认证的客户 

    l 问题客户身份和访问令牌 

    l 验证令牌 

    v 客户端

    客户端是一个软件,请求令牌从IdentityServer——对一个用户进行身份验证或 通常用于访问资源(也称为依赖方或RP)。 客户端必须注册与OP

    客户是web应用程序的例子,本地移动或桌面应用程序,水疗,服务器进程等。

    v 用户

    用户是一个人类,使用一个注册客户机来访问他或她的数据。

    v 范围

    作用域标识符为客户想要访问的资源。 这个标识符在一个发送给OP 身份验证或令牌的请求。

    默认情况下允许每个客户机请求令牌为每个范围,但是你可以限制。

    他们有两种口味。

    Ø 身份范围

    请求关于用户的标识信息(又名声称),如他的名字或电子邮件地址被建模为一个范围的OpenID连接。

    如有一个范围profile首选,包括名字、姓、用户名、性别、证件照以及更多。 你可以读到标准范围在这里你可以创建自己的范围IdentityServer模型自己的需求。

    Ø 资源范围

    资源范围确定web api(也称为资源服务器)——你可以如命名范围calendar代表你的日历API

    第三章认证/令牌请求

    客户请求令牌的相机会取决于请求的范围,OP会返回一个身份令牌,一个访问令牌,或两者兼而有之。

    v 身份标识

    可以通过客户端验证身份令牌。

    它包含有关用户的信息和细节在OP用户身份验证。 身份令牌代表一个成功的身份验证。

    v 访问令牌

    一个访问令牌可以由资源进行验证。

    客户请求访问令牌和转发他们的API。 访问令牌包含客户端和用户信息(如果存在)。 api使用这些信息来授权访问他们的数据。

    v 特性和规格

    IdentityServer实现以下规格:

    l OpenID连接核心1.0(规范)

    l 基本的、隐含的和混合流

    l OpenID连接发现1.0规范)

    l OpenID连接会话管理1.0 - 22(草案规范)

    l OAuth 2.0(RFC 6749)

    l 授权代码、隐式资源所有者密码凭证和客户端凭证

    l OAuth 2.0不记名使用令牌(RFC 6750)

    l OAuth 2.0多种反应类型(规范)

    l OAuth 2.0表单Post响应模式(规范)

    l OAuth 2.0令牌撤销(RFC 7009)

    v 包装和构建

    IdentityServernuget包的数量。

    Ø 核心

    nuget | github

    包含核心IdentityServer对象模型、服务和服务器。 只包含核心支持内存配置和用户的商店,但是你可以通过配置插件支持其他商店。 这是另一个回购和包。

    Ø 配置商店

    存储配置数据(客户和范围)以及运行时数据(同意,令牌处理,刷新令牌)

    实体框架nuget | github

    (社区贡献)MongoDbgithub

    Ø 用户存储

    支持身份管理库。

    MembershipReboot nuget | github

    ASP.Net Identity nuget | github

    Ø 插件

    协议插件。

    ws - federationnuget | github

    Ø 访问令牌验证中间件

    OWIN中间件api。 提供了一种简便的方法来验证访问令牌和实施范围的要求。

    nuget | github

    v 开发构建

    此外,我们发布dev /临时构建MyGet。 Visual Studio下面添加到你如果你想给他们一个尝试:

    https://www.myget.org/F/identity/

    Ø 创建最简单的OAuth2授权服务器,客户端和API

    这个介绍的目的是创建简单的IdentityServer安装作为一个OAuth2授权服务器。 这是应该让你开始的一些基本特性和配置选项(可以找到完整的源代码在这里)。 还有其他更高级的演练的文档之后你可以做。 本教程包括:  

    l 创建一个自托管IdentityServer 

    l 为应用程序设置客户服务通信使用应用程序帐户和代表一个用户 

    l 注册一个API 

    l 请求访问令牌 

    l 调用一个API 

    l 验证一个访问令牌

    Ø 设置IdentityServer

    首先,我们将创建一个控制台主机和设置IdentityServer

    首先创建一个标准的控制台应用程序并添加IdentityServer通过nuget:

    install-package Thinktecture.IdentityServer3

    Ø 注册的API

    api建模为范围,你需要注册所有api,你希望能够请求访问令牌。 为我们创建一个类返回的列表Scope:

    using Thinktecture.IdentityServer.Core.Models;

    static class Scopes{

        public static List<Scope> Get()

        {

            return new List<Scope>

            {

                new Scope

                {

                    Name = "api1"

                }

            };

        }}

    Ø 注册客户端

    现在我们想注册一个单一的客户端。 这个客户端可以请求的令牌api1范围。 对于我们的第一次迭代,就没有人类参与和客户端只会请求令牌 代表本身(想想机对机通信)。 稍后我们将添加一个用户。

    这个客户我们配置以下事情: 

    l 显示名称和id(唯一的名称) 

    l 客户端秘密(用于验证客户端对令牌端点) 

    l 流(客户端凭证流在这种情况下) 

    l 所谓的参考标记的使用。 参考标记不需要签名证书。 

    using Thinktecture.IdentityServer.Core.Models;

    static class Clients{

        public static List<Client> Get()

        {

            return new List<Client>

            {

                // no human involved

                new Client

                {

                    ClientName = "Silicon-only Client",

                    ClientId = "silicon",

                    Enabled = true,

                    AccessTokenType = AccessTokenType.Reference,

     

                    Flow = Flows.ClientCredentials,

                    ClientSecrets = new List<ClientSecret>

                    {

                        new ClientSecret("F621F470-9731-4A25-80EF-67A6F7C5F4B8".Sha256())

                    }

                }

            };

        }}

    Ø 配置IdentityServer

    IdentityServer被实现为OWIN中间件。 中配置的Startup类使用UseIdentityServer扩展方法。 下面的代码片段设置一个梗概服务器范围和客户。 我们还建立了一个空列表的用户,我们稍后会添加用户。

    using Thinktecture.IdentityServer.Core.Configuration;using Thinktecture.IdentityServer.Core.Services.InMemory;

    class Startup{

        public void Configuration(IAppBuilder app)

        {

            var factory = InMemoryFactory.Create(

                scopes:  Scopes.Get(),

                clients: Clients.Get(),

                users:   new List<InMemoryUser>());

     

            var options = new IdentityServerOptions

            {

                Factory = factory

            };

     

            app.UseIdentityServer(options);

        }}

    Ø 托管IdentityServer

    最后一步是主机IdentityServer。 为此我们将武士刀自托管包添加到控制台应用程序:

    install-package Microsoft.Owin.SelfHost

    添加以下代码Program.cs:

    using Microsoft.Owin.Hosting;using Thinktecture.IdentityServer.Core.Logging;

    static void Main(string[] args){

        LogProvider.SetCurrentLogProvider(new DiagnosticsTraceLogProvider());

     

        using (WebApp.Start<Startup>("https://localhost:44333"))

        {

            Console.WriteLine("server running...");

            Console.ReadLine();

        }}

    当您运行控制台应用程序,您应该看到一些诊断输出server running...

    v 添加一个API

    在这一部分,我们将添加一个简单的web API,它被配置为从IdentityServer我们只是需要一个访问令牌。

    Ø 创建网络主机

    添加一个新的ASP.NET Web Application解决方案和选择Empty选项(没有框架的引用)

    添加必要的nuget:

    install-package Microsoft.Owin.Host.SystemWeb

    install-package Microsoft.AspNet.WebApi.Owin

    install-package Thinktecture.IdentityServer3.AccessTokenValidation

    Ø 添加一个控制器

    添加这个简单的测试控制器:

    [Route("test")]public class TestController : ApiController{

        public IHttpActionResult Get()

        {

            var caller = User as ClaimsPrincipal;

     

            return Json(new

            {

                message = "OK computer",

                client =  caller.FindFirst("client_id").Value

            });

        }}

    User财产的控制器给你访问的访问令牌。

    Ø 添加启动

    添加以下Startup类设置web api和配置与IdentityServer信任

    using Thinktecture.IdentityServer.AccessTokenValidation;

    public void Configuration(IAppBuilder app){

        // accept access tokens from identityserver and require a scope of 'api1'

        app.UseIdentityServerBearerTokenAuthentication(new IdentityServerBearerTokenAuthenticationOptions

            {

                Authority = "https://localhost:44333",

                RequiredScopes = new[] { "api1" }

            });

     

        // configure web api

        var config = new HttpConfiguration();

        config.MapHttpAttributeRoutes();

     

        // require authentication for all controllers

        config.Filters.Add(new AuthorizeAttribute());

     

        app.UseWebApi(config);}

    试着打开浏览器并访问测试控制器,您应该看到一个401因为必要的访问令牌是失踪。

    v 添加一个控制台客户

    在接下来的部分,我们将添加一个简单的控制台客户端请求一个访问令牌和使用进行身份验证的api

    首先添加一个新的控制台项目和安装nugetOAuth2客户机助手库:

    install-package Thinktecture.IdentityModel.Client

    第一个代码片段使用客户端证书请求访问令牌:

    using Thinktecture.IdentityModel.Client;

    static TokenResponse GetToken(){

        var client = new OAuth2Client(

            new Uri("https://localhost:44333/connect/token"),

            "silicon",

            "F621F470-9731-4A25-80EF-67A6F7C5F4B8");

     

        return client.RequestClientCredentialsAsync("api1").Result;}

    第二个代码段调用API使用访问令牌:

    using Thinktecture.IdentityModel.Client;

    static void CallApi(TokenResponse response){

        var client = new HttpClient();

        client.SetBearerToken(response.AccessToken);

     

        Console.WriteLine(client.GetStringAsync("http://localhost:14869/test").Result);}

    如果你叫两个片段,您应该看到{"message":"OK computer","client":"silicon"}在您的控制台。

    v 添加一个用户

    到目前为止,客户端请求的访问令牌本身,没有用户。 让我们介绍一个人。

    Ø 添加一个用户服务

    用户服务管理用户,对于此示例,我们将使用简单的内存的用户服务。 首先我们需要定义一些用户:

    using Thinktecture.IdentityServer.Core.Services.InMemory;

    static class Users{

        public static List<InMemoryUser> Get()

        {

            return new List<InMemoryUser>

            {

                new InMemoryUser

                {

                    Username = "bob",

                    Password = "secret",

                    Subject = "1"

                },

                new InMemoryUser

                {

                    Username = "alice",

                    Password = "secret",

                    Subject = "2"

                }

            };

        }}

    UsernamePassword用于验证用户Subject是该用户的唯一标识符,将嵌入到访问令牌。

    Startup配置方法取代所说空的用户列表Get方法。

    替换:csharp users: new List<InMemoryUser>()); :csharp users: Users.Get());

    Ø 添加一个客户

    接下来,我们将添加一个客户端定义使用流resource owner password credential grant。 这个流允许客户端用户的用户名和密码发送到令牌服务,得到一个访问令牌。

    在总Clients类是这样的:

    using Thinktecture.IdentityServer.Core.Models;

    static class Clients{

        public static List<Client> Get()

        {

            return new List<Client>

            {

                // no human involved

                new Client

                {

                    ClientName = "Silicon-only Client",

                    ClientId = "silicon",

                    Enabled = true,

                    AccessTokenType = AccessTokenType.Reference,

     

                    Flow = Flows.ClientCredentials,

                    ClientSecrets = new List<ClientSecret>

                    {

                        new ClientSecret("F621F470-9731-4A25-80EF-67A6F7C5F4B8".Sha256())

                    }

                },

     

                // human is involved

                new Client

                {

                    ClientName = "Silicon on behalf of Carbon Client",

                    ClientId = "carbon",

                    Enabled = true,

                    AccessTokenType = AccessTokenType.Reference,

     

                    Flow = Flows.ResourceOwner,

                    ClientSecrets = new List<ClientSecret>

                    {

                        new ClientSecret("21B5F798-BE55-42BC-8AA8-0025B903DC3B".Sha256())

                    }

                }

            };

        }}

    Ø 更新的API

    当一个人类,将包含访问令牌sub惟一标识用户。 让我们把这个小修改API控制器:

    [Route("test")]public class TestController : ApiController{

        public IHttpActionResult Get()

        {

            var caller = User as ClaimsPrincipal;

     

            var subjectClaim = caller.FindFirst("sub");

            if (subjectClaim != null)

            {

                return Json(new

                {

                    message = "OK user",

                    client = caller.FindFirst("client_id").Value,

                    subject = subjectClaim.Value

                });

            }

            else

            {

                return Json(new

                {

                    message = "OK computer",

                    client = caller.FindFirst("client_id").Value

                });

            }

        }}

    Ø 更新客户端

    下添加一个新的方法给客户端请求一个访问令牌代表用户:

    static TokenResponse GetUserToken(){

        var client = new OAuth2Client(

            new Uri("https://localhost:44333/connect/token"),

            "carbon",

            "21B5F798-BE55-42BC-8AA8-0025B903DC3B");

     

        return client.RequestResourceOwnerPasswordAsync("bob", "secret", "api1").Result;}

    现在尝试两种方法的请求令牌和检查要求和API的回应。

    该项目名称为Simplest OAuth2 Walkthrough。

    v 下一步要做什么

    这演练了一个非常简单的OAuth2场景。 下一个你可以试试: 

    l 其他流,如隐式、代码或混合。 他们都是推动者等高级场景联合会和外部的身份 

    l 连接到您的用户数据库,通过编写自己的用户服务或使用我们的开箱即用的支持ASP.Net Identity和MembershipReboot 

    l 客户端和范围配置存储在一个数据存储。 我们有现成的实体框架支持。

    l 使用OpenID连接和添加身份验证和身份令牌标识范围

    l 本教程将引导您完成必要的步骤来得到一个最小IdentityServer启动并运行。 为简单起见,我们将主机IdentityServer和客户端在同一web应用程序,这可能不是一个非常现实的场景中,但可以让你开始没有使它太复杂。

    可以找到完整的源代码在这里

    v 第1部分- MVC身份验证和授权

    在第一部分,我们将创建一个简单的MVC应用程序并通过IdentityServer添加身份验证。 然后我们会仔细看看Claims,Claims转换和授权

    Ø 创建web应用程序

    Visual Studio 2013,创建一个标准的MVC应用程序和身份验证设置为无身份验证

    现在您可以切换项目SSL使用属性窗口:

    重要的别忘了更新在您的项目中开始URL属性。

    Ø 添加IdentityServer

    IdentityServer基于OWIN /刀和分布式Nuget包。 将其添加到新创建的web主机,安装以下两个方案:

    install-package Microsoft.Owin.Host.Systemweb

    install-package Thinktecture.IdentityServer3

    Ø 配置IdentityServer -客户

    IdentityServer需要一些关于客户的信息支持,这可以简单地提供使用Client对象:

    public static class Clients{

        public static IEnumerable<Client> Get()

        {

            return new[]

            {

                new Client 

                {

                    Enabled = true,

                    ClientName = "MVC Client",

                    ClientId = "mvc",

                    Flow = Flows.Implicit,

     

                    RedirectUris = new List<string>

                    {

                        "https://localhost:44319/"

                    }

                }

            };

        }}

    Ø 配置IdentityServer——用户

    接下来,我们将添加一些用户再次IdentityServer——这可以通过提供一个简单的c#类。 你可以从任何数据存储和检索用户信息我们提供开箱即用的支持ASP。 网络身份和MembershipReboot

    public static class Users{

        public static List<InMemoryUser> Get()

        {

            return new List<InMemoryUser>

            {

                new InMemoryUser

                {

                    Username = "bob",

                    Password = "secret",

                    Subject = "1",

     

                    Claims = new[]

                    {

                        new Claim(Constants.ClaimTypes.GivenName, "Bob"),

                        new Claim(Constants.ClaimTypes.FamilyName, "Smith")

                    }

                }

            };

        }}

    Ø 添加启动

    IdentityServer在启动配置类。 这里我们提供关于客户的信息,用户,范围签名证书和其他一些配置选项。 在生产中应该从Windows证书存储加载签名证书或其他担保来源。 在这个示例中,我们简单地添加它到项目文件(您可以下载测试证书在这里。 将其添加到项目并设置其构建行动Copy to output

    对信息如何加载证书从Azure网站看到在这里

    public class Startup{

        public void Configuration(IAppBuilder app)

        {

            app.Map("/identity", idsrvApp =>

                {

                    idsrvApp.UseIdentityServer(new IdentityServerOptions

                    {

                        SiteName = "Embedded IdentityServer",

                        SigningCertificate = LoadCertificate(),

     

                        Factory = InMemoryFactory.Create(

                            users  : Users.Get(),

                            clients: Clients.Get(),

                            scopes : StandardScopes.All)

                    });

                });

        }

     

        X509Certificate2 LoadCertificate()

        {

            return new X509Certificate2(

                string.Format(@"{0}inidentityServeridsrv3test.pfx", AppDomain.CurrentDomain.BaseDirectory), "idsrv3test");

        }}

    在这一点上你有一个功能齐全的IdentityServer你可以浏览发现端点检查配置:

     

    Ø RAMMFAR

    最后一件事,请不要忘了RAMMFAR添加到您的网站。 配置,否则我们的一些嵌入式资产由IIS将不能正确加载:

    <system.webServer>

      <modules runAllManagedModulesForAllRequests="true" /></system.webServer>

    v 添加和配置OpenID身份验证连接中间件

    OIDC验证添加到MVC应用程序中,我们需要添加两个包:

    install-package Microsoft.Owin.Security.Cookies

    install-package Microsoft.Owin.Security.OpenIdConnect

    在启动配置饼干中间件。 cs的默认值:

    install-package Microsoft.Owin.Security.Cookies

    install-package Microsoft.Owin.Security.OpenIdConnect 

    在启动配置饼干中间件。 cs的默认值: 配置,否则我们的一些嵌入式资产由IIS将不能正确加载:

     

    app.UseCookieAuthentication(new CookieAuthenticationOptions

        {

            AuthenticationType = "Cookies"

        });

    OpenID中间件(也在StartUp.cs)连接到我们的嵌入式版本的IdentityServer和使用先前配置的客户端配置:

    app.UseOpenIdConnectAuthentication(new OpenIdConnectAuthenticationOptions

        {

            Authority = "https://localhost:44319/identity",

            ClientId = "mvc",

            RedirectUri = "https://localhost:44319/",

            ResponseType = "id_token",

     

            SignInAsAuthenticationType = "Cookies"

        });

    v 添加一个受保护的资源和Claims

    启动认证与IdentityServer您需要创建一个受保护的资源,如通过增加全球授权过滤器。 对于我们的示例,我们将简单地保护About行动上的Home控制器。 此外我们将交出声称视图中我们可以看到号称IdentityServer发出的有:

    [Authorize]public ActionResult About(){

        return View((User as ClaimsPrincipal).Claims);}

     相应的观点是这样的:

    @model IEnumerable<System.Security.Claims.Claim>

    <dl>

        @foreach (var claim in Model)

        {

            <dt>@claim.Type</dt>

            <dd>@claim.Value</dd>

        }</dl>

    Ø 身份验证和Claims

    点击链接将触发身份验证。 IdentityServer将显示登录屏幕并发送一个令牌回主应用程序。 OpenID Connect中间件验证令牌,提取并将其传送给Cookies中间件,这将反过来设置身份验证cookie。 用户现在签署。  

    Ø 添加角色和范围

    在下一步我们要添加一些声称我们的用户角色,稍后我们将使用授权。

    现在我们得到了OIDC标准范围,让我们定义一个角色范围,包括角色要求并添加到标准范围:

    public static class Scopes{

        public static IEnumerable<Scope> Get()

        {

            var scopes = new List<Scope>

            {

                new Scope

                {

                    Enabled = true,

                    Name = "roles",

                    Type = ScopeType.Identity,

                    Claims = new List<ScopeClaim>

                    {

                        new ScopeClaim("role")

                    }

                }

            };

     

            scopes.AddRange(StandardScopes.All);

     

            return scopes;

        }}

    也改变了工厂Startup

    Factory = InMemoryFactory.Create(

        users: Users.Get(),

        clients: Clients.Get(),

        scopes: Scopes.Get())

    接下来,我们添加两个角色声称鲍勃:

    public static class Users{

        public static IEnumerable<InMemoryUser> Get()

        {

            return new[]

            {

                new InMemoryUser

                {

                    Username = "bob",

                    Password = "secret",

                    Subject = "1",

     

                    Claims = new[]

                    {

                        new Claim(Constants.ClaimTypes.GivenName, "Bob"),

                        new Claim(Constants.ClaimTypes.FamilyName, "Smith"),

                        new Claim(Constants.ClaimTypes.Role, "Geek"),

                        new Claim(Constants.ClaimTypes.Role, "Foo")

                    }

                }

            };

        }}

    Ø 改变了中间件配置要求的角色

    默认情况下,OIDC中间件要求两个范围:openidprofile——这就是为什么IdentityServer包括Claims主体和名称。 现在我们添加一个请求roles范围:

    app.UseOpenIdConnectAuthentication(new OpenIdConnectAuthenticationOptions

        {

            Authority = "https://localhost:44319/identity",

     

            ClientId = "mvc",

            Scope = "openid profile roles",

            RedirectUri = "https://localhost:44319/",

            ResponseType = "id_token",

     

            SignInAsAuthenticationType = "Cookies"

        });

    成功的身份验证之后,现在您应该看到角色收集Claims在用户的Claims:

     

    Ø 要求转换

    检查声明这个页面时,您将注意到两件事:一些声称奇怪的长类型名称和有更多的比你可能需要在您的应用程序。

    长的声明名称来自微软的JWT处理程序试图一些Claims类型映射到.netClaimTypes类类型。 你可以关掉这个行为与以下代码行(Startup)

    这也意味着你需要调整配置anti-CSRF保护新的独特sub申请类型:

    AntiForgeryConfig.UniqueClaimTypeIdentifier = Constants.ClaimTypes.Subject;JwtSecurityTokenHandler.InboundClaimTypeMap = new Dictionary<string, string>();

    声称将现在看起来像这样:

    这是一个进步,但是仍有一些低水平协议声称,当然是不需要通过典型的业务逻辑。 将原始输入Claims转化为特定于应用程序的过程Claims称为Claims转换。 在这个过程中你把传入的说法,决定你想要号称也许需要联系其他数据存储检索更声称所需的应用程序。

    OIDC中间件有通知,您可以使用它们来做Claims转型——由此产生的Claims将存储在cookie:

    app.UseOpenIdConnectAuthentication(new OpenIdConnectAuthenticationOptions

        {

            Authority = "https://localhost:44319/identity",

     

            ClientId = "mvc",

            Scope = "openid profile roles",

            RedirectUri = "https://localhost:44319/",

            ResponseType = "id_token",

     

            SignInAsAuthenticationType = "Cookies",

            UseTokenLifetime = false,

     

            Notifications = new OpenIdConnectAuthenticationNotifications

            {

                SecurityTokenValidated = async n =>

                    {

                        var id = n.AuthenticationTicket.Identity;

     

                        // we want to keep first name, last name, subject and roles

                        var givenName = id.FindFirst(Constants.ClaimTypes.GivenName);

                        var familyName = id.FindFirst(Constants.ClaimTypes.FamilyName);

                        var sub = id.FindFirst(Constants.ClaimTypes.Subject);

                        var roles = id.FindAll(Constants.ClaimTypes.Role);

     

                        // create new identity and set name and role claim type

                        var nid = new ClaimsIdentity(

                            id.AuthenticationType,

                            Constants.ClaimTypes.GivenName,

                            Constants.ClaimTypes.Role);

     

                        nid.AddClaim(givenName);

                        nid.AddClaim(familyName);

                        nid.AddClaim(sub);

                        nid.AddClaims(roles);

     

                        // add some other app specific claim

                        nid.AddClaim(new Claim("app_specific", "some data"));                   

     

                        n.AuthenticationTicket = new AuthenticationTicket(

                            nid,

                            n.AuthenticationTicket.Properties);

                    }

            }

        });

    添加上面的代码之后,我们要求的设置现在看起来像这样:

    Ø 授权

    现在我们已经认证和一些Claims,我们可以开始添加简单的授权规则。

    MVC有一个内置的属性[Authorize]需要通过身份验证的用户,您还可以使用此属性来标注角色从属关系的要求。 我们不推荐这种方法,因为这通常会导致代码混合业务/控制器逻辑和授权策略的担忧。 我们不推荐从控制器分离授权逻辑导致清洁代码和更好的可测试性(阅读更多在这里)

    Ø 资源授权

    添加新授权的基础设施和新属性,我们添加一个Nuget

    install-package Thinktecture.IdentityModel.Owin.ResourceAuthorization.Mvc

    接下来我们注释Contact行动上的Home控制器和一个属性,表示这一行动将执行ReadContactDetails资源:

    [ResourceAuthorize("Read", "ContactDetails")]public ActionResult Contact(){

        ViewBag.Message = "Your contact page.";

     

        return View();}

    注意,属性不表示允许读取联系人是谁——我们逻辑移到一个单独的授权经理知道行动,资源和谁可以做哪个操作在你的应用程序:

    public class AuthorizationManager : ResourceAuthorizationManager{

        public override Task<bool> CheckAccessAsync(ResourceAuthorizationContext context)

        {

            switch (context.Resource.First().Value)

            {

                case "ContactDetails":

                    return AuthorizeContactDetails(context);

                default:

                    return Nok();

            }

        }

     

        private Task<bool> AuthorizeContactDetails(ResourceAuthorizationContext context)

        {

            switch (context.Action.First().Value)

            {

                case "Read":

                    return Eval(context.Principal.HasClaim("role", "Geek"));

                case "Write":

                    return Eval(context.Principal.HasClaim("role", "Operator"));

                default:

                    return Nok();

            }

        }}

    最后我们授权管理器连接到OWIN管道Startup:

    app.UseResourceAuthorization(new AuthorizationManager());

    运行示例和步骤通过代码来熟悉流。

    Ø 角色权限

    然而,如果你选择使用[Authorize(Roles = "Foo,Bar")]要知道网站可以被扔进一个无限重定向循环当当前用户身份验证,但不属于其中的一个角色或用户进入AuthorizeMVC 5.2属性(验证)。 这是因为不良的结果Authorize属性将行动的结果是401年未授权用户身份验证,而不是一个的角色。 401结果触发重定向与IdentityServer进行身份验证,验证,然后将用户重定向回,然后重定向循环开始。 这种行为可以克服通过重写Authorize属性的HandleUnauthorizedRequest方法如下,然后使用自定义授权属性相反的MVC

    // Customized authorization attribute:public class AuthAttribute : AuthorizeAttribute{

        protected override void HandleUnauthorizedRequest(AuthorizationContext filterContext)

        {

            if (filterContext.HttpContext.User.Identity.IsAuthenticated)

            {

                // 403 we know who you are, but you haven't been granted access

                filterContext.Result = new HttpStatusCodeResult(System.Net.HttpStatusCode.Forbidden);

            }

            else

            {

                // 401 who are you? go login and then try again

                filterContext.Result = new HttpUnauthorizedResult();

            }

        }}

    // Usage:[Auth(Roles = "Geek")]public ActionResult About(){

        // ...}

    Ø 更多的授权和处理拒绝访问场景

    让我们做更多授权通过添加一个新的操作方法Home控制器

    [ResourceAuthorize("Write", "ContactDetails")]public ActionResult UpdateContact(){

        ViewBag.Message = "Update your contact details!";

     

        return View();}

    当你试图调用,导航到采取行动/home/updatecontact您将看到一个URLforbidden错误页面。

     

    事实上你会看到不同的响应基于如果用户已经通过身份验证。 如果不是MVC将重定向到登录页面,如果身份验证,您将看到禁止响应。 这是由设计(阅读更多在这里)

    你可以通过检查处理被禁止的条件403状态码,我们提供这样一个开箱即用的过滤:

    [ResourceAuthorize("Write", "ContactDetails")][HandleForbidden]public ActionResult UpdateContact(){

        ViewBag.Message = "Update your contact details!";

     

        return View();}

    HandleForbidden过滤器(当然也可以全球)将重定向到指定的视图时默认403得到了释放,我们寻找一个视图Forbidden

    您还可以使用授权经理命令式地,给你更多的选择:

    [HandleForbidden]public ActionResult UpdateContact(){

        if (!HttpContext.CheckAccess("Write", "ContactDetails", "some more data"))

        {

            // either 401 or 403 based on authentication state

            return this.AccessDenied();

        }

     

        ViewBag.Message = "Update your contact details!";

        return View();}

    Ø 添加注销

    添加注销很容易,只需添加一个新的操作调用Signout方法在武士刀的身份验证管理器:

    public ActionResult Logout(){

        Request.GetOwinContext().Authentication.SignOut();

        return Redirect("/");}

    这将启动所谓的往返endsessionIdentityServer端点。 这个端点将清晰的身份验证cookie和终止会话:

    通常最安全的事情现在会简单地关闭浏览器窗口摆脱所有的会话数据。 一些应用程序虽然想给用户一个机会返回一个匿名用户。

    这是可能的,但需要一些步骤,首先需要注册一个有效的URL注销手续完成后返回。 这样做是在客户端定义MVC应用程序(注意新PostLogoutRedirectUris设置):

    new Client {

        Enabled = true,

        ClientName = "MVC Client",

        ClientId = "mvc",

        Flow = Flows.Implicit,

     

        RedirectUris = new List<string>

        {

            "https://localhost:44319/"

        },

        PostLogoutRedirectUris = new List<string>

        {

            "https://localhost:44319/"

        }}

    下一个客户端必须证明其身份注销端点,以确保我们重定向到正确的URL(而不是一些垃圾信息散布者/钓鱼页面)。 这是通过发送回最初的身份令牌,客户端身份验证过程中收到了。 到目前为止,我们已经丢弃这个令牌,现在是时候改变转换逻辑来保护它。

    这是通过将这行代码添加到我们的SecurityTokenValidated

    // keep the id_token for logoutnid.AddClaim(new Claim("id_token", n.ProtocolMessage.IdToken));

    最后一步,我们必须把id_token当用户退出系统时,我们让IdentityServer往返。 这也是使用通知OIDC中间件:

    RedirectToIdentityProvider = async n =>

        {

            if (n.ProtocolMessage.RequestType == OpenIdConnectRequestType.LogoutRequest)

            {

                var idTokenHint = n.OwinContext.Authentication.User.FindFirst("id_token").Value;

                n.ProtocolMessage.IdTokenHint = idTokenHint;

            }

        }

    这些变化,IdentityServer会给用户一个链接返回到调用应用程序: 

    Ø 添加谷歌身份验证

    接下来,我们想启用外部身份验证。 这是通过添加额外的武士刀认证中间件IdentityServer——在我们的例子中,我们将使用谷歌。

    注册IdentityServer与谷歌

    首先我们需要注册IdentityServer在谷歌的开发者控制台。 这包括几个步骤。

    首先导航到:

    https://console.developers.google.com

    l 创建一个新项目 

    l 使Google + API 

    l 同意屏幕配置电子邮件地址和产品名称 

    l 创建一个客户端应用程序

     

    在您创建客户端应用程序,开发人员控制台将显示您客户id和一个客户的秘密。 我们需要这两个值后,我们配置谷歌中间件。

    添加谷歌身份验证中间件

    添加中间件通过安装以下方案:

    install-package Microsoft.Owin.Security.Google

    配置中间件

    添加下面的方法Startup:

    private void ConfigureIdentityProviders(IAppBuilder app, string signInAsType){

        app.UseGoogleAuthentication(new GoogleOAuth2AuthenticationOptions

            {

                AuthenticationType = "Google",

                Caption = "Sign-in with Google",

                SignInAsAuthenticationType = signInAsType,

     

                ClientId = "...",

                ClientSecret = "..."

            });}

    private void ConfigureIdentityProviders(IAppBuilder app, string signInAsType){

        app.UseGoogleAuthentication(new GoogleOAuth2AuthenticationOptions

            {

                AuthenticationType = "Google",

                Caption = "Sign-in with Google",

                SignInAsAuthenticationType = signInAsType,

     

                ClientId = "...",

                ClientSecret = "..."

            });}

    接下来我们点IdentityServer选项类这个方法:

    idsrvApp.UseIdentityServer(new IdentityServerOptions{

        SiteName = "Embedded IdentityServer",

        SigningCertificate = LoadCertificate(),

     

        Factory = InMemoryFactory.Create(

            users: Users.Get(),

            clients: Clients.Get(),

            scopes: Scopes.Get()),

     

        AuthenticationOptions = new Thinktecture.IdentityServer.Core.Configuration.AuthenticationOptions

        {

            IdentityProviders = ConfigureIdentityProviders

        }});

    就是这样用户下次登录时,会有一个登录页面上的与谷歌登录按钮:

    请注意,role与谷歌声称丢失时登录。 很有意义,因为谷歌没有角色的概念。 做好准备,并非所有的身份提供者将提供相同的声明类型。

    v 第2部分-添加和调用Web API

    在这部分我们将添加一个Web API的解决方案。 API将由IdentityServer担保。 接下来我们的MVC应用程序将调用API使用信任子系统和身份代表团的方法。

    Ø 添加Web API项目

    最简单的方法创建一个干净的API项目是通过添加一个空的web项目。

    接下来,我们将添加Web API和武士刀使用Nuget托管:

    install-package Microsoft.Owin.Host.SystemWeb

    install-package Microsoft.Aspnet.WebApi.Owin

    Ø 添加一个测试控制器

    下面的控制器将返回所有Claims回调用者——这将使我们能够检查将发送到API的令牌。

    [Route("identity")][Authorize]public class IdentityController : ApiController{

        public IHttpActionResult Get()

        {

            var user = User as ClaimsPrincipal;

            var claims = from c in user.Claims

                            select new

                            {

                                type = c.Type,

                                value = c.Value

                            };

     

            return Json(claims);

        }}

    Ø 连接在启动Web API和安全

    一如既往地Katana-based托管,发生在所有配置Startup:

    public class Startup{

        public void Configuration(IAppBuilder app)

        {

            // web api configuration

            var config = new HttpConfiguration();

            config.MapHttpAttributeRoutes();

     

            app.UseWebApi(config);

        }}

    另外我们想确保我们的API使用IdentityServer——需要两件事:

    l 只接受IdentityServer发行的令牌

    l 只接受令牌,发布了,我们会给我们的API——API的名称sampleApi(也称为scope)

    完成,我们添加一个Nuget:

    install-package Thinktecture.IdentityServer3.AccessTokenValidation

    . . 并使用它们Startup:

    public class Startup{

        public void Configuration(IAppBuilder app)

        {

            app.UseIdentityServerBearerTokenAuthentication(new IdentityServerBearerTokenAuthenticationOptions

            {

                Authority = "https://localhost:44319/identity",

                RequiredScopes = new[] { "sampleApi" }

            });

     

            // web api configuration

            var config = new HttpConfiguration();

            config.MapHttpAttributeRoutes();

     

            app.UseWebApi(config);

        }}

    请注意IdentityServer问题标准JSON Web标记(JWT),你可以使用普通的武士刀JWT中间件来验证它们。 我们的中间件只是一个方便因为它可以自动配置本身使用IdentityServer发现文档(元数据)

    Ø 注册在IdentityServer API

    接下来我们需要注册的API——这是通过扩展范围。 这一次我们添加一个所谓的资源范围:

    public static class Scopes{

        public static IEnumerable<Scope> Get()

        {

            var scopes = new List<Scope>

            {

                new Scope

                {

                    Enabled = true,

                    Name = "roles",

                    Type = ScopeType.Identity,

                    Claims = new List<ScopeClaim>

                    {

                        new ScopeClaim("role")

                    }

                },

                new Scope

                {

                    Enabled = true,

                    Name = "sampleApi",

                    Description = "Access to a sample API",

                    Type = ScopeType.Resource

                }

            };

     

            scopes.AddRange(StandardScopes.All);

     

            return scopes;

        }}

    Ø 注册一个Web API客户端

    接下来我们将调用API。 你可以为使用客户端凭据(如服务帐户)或通过授权用户的身份。 我们将开始与客户凭证。

    首先我们需要注册一个新客户的MVC应用程序。出于安全原因,IdentityServer只允许每个客户一个流因为我们现有MVC客户已经使用隐式流程,我们需要创建一个新的客户服务的服务通信。

    public static class Clients{

        public static IEnumerable<Client> Get()

        {

            return new[]

            {

                new Client 

                {

                    Enabled = true,

                    ClientName = "MVC Client",

                    ClientId = "mvc",

                    Flow = Flows.Implicit,

     

                    RedirectUris = new List<string>

                    {

                        "https://localhost:44319/"

                    }

                },

                new Client

                {

                    Enabled = true,

                    ClientName = "MVC Client (service communication)",

                    ClientId = "mvc_service",

                    ClientSecrets = new List<ClientSecret>

                    {

                        new ClientSecret("secret".Sha256())

                    },

     

                    Flow = Flows.ClientCredentials

                }

            };

        }}

    Ø 调用API

    调用API包含两个部分:

    l 请求令牌从IdentityServer API使用客户端证书

    l 调用API使用访问令牌

    简化与OAuth2令牌的交互端点,添加客户端包通过Nuget MVC项目:

    install-package Thinktecture.IdentityModel.Client

    在控制器中,添加新类CallApiController。 下面的代码片段的令牌的请求sampleApi

    private async Task<TokenResponse> GetTokenAsync(){

        var client = new OAuth2Client(

            new Uri("https://localhost:44319/identity/connect/token"),

            "mvc_service",

            "secret");

     

        return await client.RequestClientCredentialsAsync("sampleApi");}

    private async Task<TokenResponse> GetTokenAsync(){

        var client = new OAuth2Client(

            new Uri("https://localhost:44319/identity/connect/token"),

            "mvc_service",

            "secret");

     

        return await client.RequestClientCredentialsAsync("sampleApi");}

    而以下代码片段调用我们的身份端点使用请求的访问令牌:

    private async Task<string> CallApi(string token){

        var client = new HttpClient();

        client.SetBearerToken(token);

     

        var json = await client.GetStringAsync("https://localhost:44321/identity");

        return JArray.Parse(json).ToString();}

    把所有在一起,新添加的控制器调用服务并显示生成的声称在一个视图:

    public class CallApiController : Controller{

        // GET: CallApi/ClientCredentials

        public async Task<ActionResult> ClientCredentials()

        {

            var response = await GetTokenAsync();

            var result = await CallApi(response.AccessToken);

     

            ViewBag.Json = result;

            return View("ShowApiResult");

        }

     

        // helpers omitted}

    public class CallApiController : Controller{

        // GET: CallApi/ClientCredentials

        public async Task<ActionResult> ClientCredentials()

        {

            var response = await GetTokenAsync();

            var result = await CallApi(response.AccessToken);

     

            ViewBag.Json = result;

            return View("ShowApiResult");

        }

     

        // helpers omitted}

    结果是这样的:

    换句话说- API知道调用者:

    l 发行人名称、观众和过期(使用的令牌验证中间件)

    l 令牌的范围(范围验证使用的中间件)发布

    l 客户机id

    所有Claims的令牌将变成一个ClaimsPrincipal和通过.User控制器上的属性。

    Ø 代表用户调用的API

    接下来我们要调用API使用用户的身份。 这是通过添加完成sampleApi范围的名单范围的OpenID连接中间件配置。 我们还需要表明我们想请求一个访问令牌通过改变反应类型:

    Scope = "openid profile roles sampleApi",ResponseType = "id_token token"

    尽快的响应类型token请求IdentityServer停止,包括Claims的身份令牌。 这是优化的目的,因为你现在有一个访问令牌,允许从用户信息检索的端点,同时保持身份令牌小。

    访问端点并不难——用户信息UserInfoClient类可以使它更简单。 除了我们现在也将访问令牌存储在cookie,所以我们可以使用它当我们想访问API代表用户:

    SecurityTokenValidated = async n =>

        {

            var nid = new ClaimsIdentity(

                n.AuthenticationTicket.Identity.AuthenticationType,

                Constants.ClaimTypes.GivenName,

                Constants.ClaimTypes.Role);

     

            // get userinfo data

            var userInfoClient = new UserInfoClient(

                new Uri(n.Options.Authority + "/connect/userinfo"),

                n.ProtocolMessage.AccessToken);

     

            var userInfo = await userInfoClient.GetAsync();

            userInfo.Claims.ToList().ForEach(ui => nid.AddClaim(new Claim(ui.Item1, ui.Item2)));

     

            // keep the id_token for logout

            nid.AddClaim(new Claim("id_token", n.ProtocolMessage.IdToken));

     

            // add access token for sample API

            nid.AddClaim(new Claim("access_token", n.ProtocolMessage.AccessToken));

     

            // keep track of access token expiration

            nid.AddClaim(new Claim("expires_at", DateTimeOffset.Now.AddSeconds(int.Parse(n.ProtocolMessage.ExpiresIn)).ToString()));

     

            // add some other app specific claim

            nid.AddClaim(new Claim("app_specific", "some data"));

     

            n.AuthenticationTicket = new AuthenticationTicket(

                nid,

                n.AuthenticationTicket.Properties);

        }

    另一个选择是重新配置IdentityServer并设置的范围AlwaysIncludeInIdToken国旗声称力范围包含声称的身份令牌,我把它留给读者作为练习。

    调用API

    由于访问令牌现在存储在cookie,我们可以简单地从Claims本金和用它来检索它调用服务:

    // GET: CallApi/UserCredentialspublic async Task<ActionResult> UserCredentials(){

        var user = User as ClaimsPrincipal;

        var token = user.FindFirst("access_token").Value;

        var result = await CallApi(token);

     

        ViewBag.Json = result;

        return View("ShowApiResult");}

    在结果页面上,您现在可以看到subClaims包括在内,这意味着API现在工作代表一个用户:

    如果你现在添加范围要求rolesampleApi范围——用户的角色将被包括在访问令牌:

    new Scope{

        Enabled = true,

        Name = "sampleApi",

        Description = "Access to a sample API",

        Type = ScopeType.Resource,

     

        Claims = new List<ScopeClaim>

        {

            new ScopeClaim("role")

        }}

            新 ScopeClaim(“角色”)

        }}

    第四章配置

    v 概述

    IdentityServer3打包为中间件和使用典型的选项模式配置:

    public void Configuration(IAppBuilder appBuilder){

        var options = new IdentityServerOptions

        {

            SigningCertificate = Certificate.Get(),

            Factory = factory,

        };

     

        appBuilder.UseIdentityServer(options);}

    IdentityServerOptionsIdentityServer类包含所有配置。 一部分由发行人的简单属性名称或网站标题,您可以从任何你认为合适的来源(静态代码中,配置文件或数据库)。 另一部分是所谓的服务工厂充当注册IdentityServer内部处理的某些方面。

    托管在IIS和RAMMFAR

    网页提供的文件作为嵌入式IdentityServer议会内部的资产。 当在IIS托管或IIS Express允许这些文件送达RAMMFAR(runAllManagedModulesForAllRequests)需要在web . config中启用:

    <system.webServer>

      <modules runAllManagedModulesForAllRequests="true">

      </modules>

    </system.webServer>

    看到样品

    v IdentityServer选项

    IdentityServerOptions 的顶层容器IdentityServer的所有配置设置。

    • IssuerUri
      • 唯一的名称服务器实例,如https://myissuer.com。 默认为IdentityServer安装的基URL
    • SiteName
      • 网站的显示名称中使用标准的观点。
    • SigningCertificate
      • X。 509证书(和相应的私钥)签署安全令牌。
    • SecondarySigningCertificate
      • 二级证书,会发现文档。 可用于客户准备证书翻转。
    • RequireSsl
      • 表明如果SSL IdentityServer所需。 默认为true。
    • PublicOrigin
      • 默认情况下,IdentityServer使用主机、协议和端口从HTTP请求在创建链接。 这可能不是准确在反向代理或负载平衡情况。 你可以覆盖原点用于链接生成使用这个属性。
    • Endpoints
      • 允许启用或禁用特定的端点(默认情况下所有端点是启用)。
    • Factory(必需)
    • DataProtector
      • 自定义数据集的保护者。 在默认情况下使用武士刀主机数据保护。
    • AuthenticationOptions
    • PluginConfiguration
      • 允许添加协议插件如ws - federation支持。
    • CorsPolicy
    • CspOptions
    • ProtocolLogoutUrls
      • 配置回调url应该被称为在注销(主要用于协议插件)。
    • LoggingOptions
    • EventsOptions
    • EnableWelcomePage
      • 启用或禁用默认的欢迎页面。 默认为true。

    v 服务工厂

    IdentityServer3包含许多功能实现OpenIDOAuth2连接。 许多这些功能设计,这样他们就可以被取代。 这将是有用的场景缺省逻辑不匹配托管应用程序的需求,或者只是应用程序希望提供一个完全不同的实现。 事实上,有些在IdentityServer3扩展点,需要提供的托管应用程序(如存储配置数据或验证的身份管理实现用户的凭证)

    Thinktecture.IdentityServer.Core.Configuration.IdentityServerServiceFactory拥有所有这些构建块,必须提供在启动时使用IdentityServerOptions(在这里有关配置选项的更多信息)

    扩展点分为三类。

    v 强制性的

    • UserService
    • ScopeStore
      • 实现检索范围配置数据
    • ClientStore
      • 实现客户端配置数据的检索

    InMemoryFactory允许设置一个服务工厂通过为用户提供内存存储,客户和范围(在这里)

    v 强制性的生产场景(但默认内存中实现)

    • AuthorizationCodeStore
      • 实现存储和检索的授权码(接口)
    • TokenHandleStore
      • 实现存储和检索的处理令牌(供参考接口)
    • RefreshTokenStore
      • 实现存储和检索的刷新令牌(接口)
    • ConsentStore
      • 实现存储和检索的同意决定(接口)
    • ViewService
      • 实现检索界面的资产。 默认使用嵌入的资产。 (接口)
    • TokenService
      • 实现创建身份和访问令牌(接口)
    • ClaimsProvider
      • 实现检索申请身份和访问令牌(接口)
    • TokenSigningService
      • 实现创建和签署安全令牌(接口)
    • CustomGrantValidator
      • 实现自定义的验证授权类型(接口)
    • CustomRequestValidator
      • 实现自定义额外的授权和验证令牌请求(接口)
    • RefreshTokenService
      • 实现创建和更新刷新令牌(接口)
    • ExternalClaimsFilter
      • 实现过滤和转换申请外部身份提供者(接口)
    • CustomTokenValidator
      • 实现自定义令牌的其他验证令牌验证端点(接口)
    • ConsentService
      • 实现逻辑同意的决定(接口)
    • ClientPermissionsService
      • 实现了检索和撤销批准,引用和刷新令牌(接口)
    • EventService
      • 实现将事件转发给一些日志记录系统(如弹性搜索)(接口)
    • RedirectUriValidator
      • 实现验证重定向和注销后uri(接口)
    • LocalizationService
      • 实现显示的本地化字符串(接口)
    • ClientSecretValidator
      • 实现验证客户的秘密(接口)
    • CorsPolicyService
      • 实现了歌珥政策(接口)

    v 可选(可以更换,但有默认的实现)

    看到在这里有关注册定制服务的更多信息和存储的实现。

    v 内存中的工厂

    内存厂是一个简单的方法来得到一个测试/ dev版本IdentityServer启动并运行。

    当你创建in-mem工厂你可以提供一个静态列表Client, ScopeInMemoryUser。 所有其他数据(e。 g同意,刷新令牌,引用标记,代码等等)将只保存在内存中。 这只是适合测试和开发。

    var factory = InMemoryFactory.Create(

        users:   Users.Get(),

        clients: Clients.Get(),

        scopes:  Scopes.Get());

    var idsrvOptions = new IdentityServerOptions{

        Factory = factory,

        SigningCertificate = Cert.Load(),};

    app.UseIdentityServer(idsrvOptions);

    第五章客户

    Client类模型OpenID连接或OAuth2客户机——例如一个本地应用程序中,web应用程序或者JS-based应用程序(链接)

    • Enabled
      • 指定是否启用客户机。 默认为true。
    • ClientId
      • 惟一的ID的客户端
    • ClientSecrets
      • 列表,客户需要一个秘密的秘密,只有相关的流
    • ClientName
      • 客户端显示名称(用于日志记录和同意屏幕)
    • ClientUri
      • URI进一步信息客户端(同意屏幕上使用)
    • LogoUri
      • URI端标志(同意屏幕上使用)
    • RequireConsent
      • 指定是否需要同意屏幕。 默认为true。
    • AllowRememberConsent
      • 指定用户可以选择存储是否同意的决定。 默认为true。
    • Flow
      • 指定允许客户机(流AuthorizationCode, Implicit, Hybrid, ResourceOwner,ClientCredentials或Custom)。 默认为Implicit。
    • AllowClientCredentialsOnly
      • 获取或设置一个值指示是否允许该客户端请求令牌只使用客户端证书。 这是很有用的,例如,你想要一个客户机能够使用一个以用户为中心流隐式和另外客户端凭证流。 默认值为false。 应该只用于机密客户(如不隐式)。
    • RedirectUris
      • 指定允许的uri返回标记或授权代码
    • PostLogoutRedirectUris
      • 注销后重定向到指定允许的uri
    • ScopeRestrictions
      • 指定范围,允许客户端请求。 如果空,客户端可以请求作用域。 默认为空集合。
    • IdentityTokenLifetime
      • 一生的身份令牌在几秒钟内(默认为300秒/ 5分钟)
    • AccessTokenLifetime
      • 一生的访问令牌在几秒钟内(默认为3600秒/ 1小时)
    • AuthorizationCodeLifetime
      • 一生的授权代码秒(默认为300秒/ 5分钟)
    • AbsoluteRefreshTokenLifetime
      • 一生最大的刷新令牌在几秒钟内。 默认为2592000秒/ 30天
    • SlidingRefreshTokenLifetime
      • 滑动的一生秒刷新令牌。 默认为1296000秒/ 15天
    • RefreshTokenUsage
      • ReUse:刷新令牌处理刷新令牌时将保持不变
      • OneTime:刷新令牌处理刷新令牌时将被更新
    • RefreshTokenExpiration
      • Absolute:刷新令牌将到期在一个固定的时间点(AbsoluteRefreshTokenLifetime指定的)
      • Sliding:当刷新令牌,将再次刷新令牌的生命周期(通过SlidingRefreshTokenLifetime中指定的数量)。 一生不会超过AbsoluteRefreshTokenLifetime。
    • AccessTokenType
      • 指定是否访问令牌是一个参考标记或自包含JWT令牌(默认Jwt)。
    • EnableLocalLogin
      • 指定如果这个客户端可以使用本地帐户,或外部国内流离失所者。 默认为true。
    • IdentityProviderRestrictions
      • 指定与该客户端可以使用外部的国内流离失所者(如果允许列表为空所有国内流离失所者)。 默认为空。
    • IncludeJwtId
      • 指定JWT访问令牌是否应该有一个嵌入式惟一的ID(通过jtiClaims)。
    • Claims
      • 允许设置申请客户端(将包括在访问令牌)。
    • AlwaysSendClientClaims
      • 如果设置,客户声称将发送每流。 如果没有,只有客户端凭证流(默认false)
    • PrefixClientClaims
      • 如果设置,所有客户端将前缀client_确保他们不意外碰撞与用户要求。 默认是true。
    • CustomGrantTypeRestrictions
      • 列表允许自定义授权类型被设置为当流Custom。 如果列表为空,所有自定义授权类型是允许的。 默认为空。

    除了有很多设置控制刷新令牌——看到的行为在这里

    Ø 例如:为隐式流配置客户端

    var client = new Client{

        ClientName = "JS Client",

        Enabled = true,

     

        ClientId = "implicitclient",

        Flow = Flows.Implicit,

     

        RequireConsent = true,

        AllowRememberConsent = true,

     

        RedirectUris = new List<string>

        {

            "https://myapp/callback.html"

        },

     

        PostLogoutRedirectUris = new List<string>

        {

            "http://localhost:23453/index.html"

        }}

    var client = new Client{

        ClientName = "JS Client",

        Enabled = true,

     

        ClientId = "implicitclient",

        Flow = Flows.Implicit,

     

        RequireConsent = true,

        AllowRememberConsent = true,

     

        RedirectUris = new List<string>

        {

            "https://myapp/callback.html"

        },

     

        PostLogoutRedirectUris = new List<string>

        {

            "http://localhost:23453/index.html"

        }}

    Ø 例如:为资源所有者流配置客户端

    var client = new Client{

        ClientName = "Legacy Client",

        Enabled = true,

     

        ClientId = "legacy",

        ClientSecrets = new List<ClientSecret>

        {

            new ClientSecret("4C701024-0770-4794-B93D-52B5EB6487A0".Sha256())

        },

     

        Flow = Flows.ResourceOwner,

     

        AbsoluteRefreshTokenLifetime = 86400,

        SlidingRefreshTokenLifetime = 43200,

        RefreshTokenUsage = TokenUsage.OneTimeOnly,

        RefreshTokenExpiration = TokenExpiration.Sliding}

    v 范围和Claims

    Ø Thinktecture.IdentityServer.Core.Models.Scope类的模型OpenID连接或OAuth2范围。

    • Enabled
      • 表明如果启用了范围,可以请求。 默认为true。
    • Name
      • 范围的名称。 这是客户机将使用请求的值范围。
    • DisplayName
      • 为同意屏幕显示名称。
    • Description
      • 屏幕描述同意。
    • Required
      • 指定用户是否可以取消屏幕上同意的范围。 默认为false。
    • Emphasize
      • 指定是否同意屏幕将会强调这个范围。 使用此设置敏感或重要的范围。 默认为false。
    • Type
      • 要么Identity(OpenID连接相关)或Resource(OAuth2参考资料)。 默认为Resource。
    • Claims
      • 列表用户声称应该纳入到身份(身份范围)或访问令牌(资源范围)。
    • IncludeAllClaimsForUser
      • 如果启用,所有的用户将被包含在令牌。 默认为false。
    • ClaimsRule
      • 确定哪些赔偿规则应该被包括在令牌(这是特定于实现的)
    • ShowInDiscoveryDocument
      • 指定是否发现文档中显示这个范围。 默认为true。

    还可以指定范围声称进入相应的令牌-ScopeClaim类具有以下属性:

    • Name
      • Claims的名称
    • Description
      • 要求的描述
    • AlwaysIncludeInIdToken
      • 指定是否这种说法应该出现在身份令牌(即使一个访问令牌请求)。 只适用于标识范围。 默认为false。

    的例子role身份范围:

    var roleScope = new Scope{

        Name = "roles",

        DisplayName = "Roles",

        Description = "Your organizational roles",

        Type = ScopeType.Identity,

     

        Claims = new List<ScopeClaim>

        {

            new ScopeClaim(Constants.ClaimTypes.Role, alwaysInclude: true)

        }};

    “AlwaysIncludeInIdentityToken”属性指定一个特定的应该是身份标识的一部分,即使用户信息的访问令牌端点的请求。

    例子的范围IdentityManager API:

    var idMgrScope = new Scope{

        Name = "idmgr",

        DisplayName = "Thinktecture IdentityManager",

        Type = ScopeType.Resource,

        Emphasize = true,

     

        Claims = new List<ScopeClaim>

        {

            new ScopeClaim(Constants.ClaimTypes.Name),

            new ScopeClaim(Constants.ClaimTypes.Role)

        }};

    v 身份验证选项

    这个话题可以找到的样本在这里

    AuthenticationOptions是一个属性的吗IdentityServerOptions自定义登录和注销的观点和行为。

    • EnableLocalLogin
      • 表明如果IdentityServer将允许用户与本地帐户进行身份验证。 禁用此设置不会显示用户名/密码表单登录页面。 这也将禁用资源所有者密码流。 默认为true。
    • EnableLoginHint
      • 指示是否login_hint参数是用于预填充用户名字段。 默认为true。
    • LoginPageLinks
      • 的列表LoginPageLink对象。 这些允许login视图提供用户自定义到其他web页面的链接,他们可能需要访问之前登录(如注册页面,或密码重置页面)。
      • LoginPageLink包含:
        • Text:出现在链接的文本。
        • Href:的URLhref的链接。
      • 自定义web页面所代表的LoginPageLink将提供的托管应用程序。 一旦执行它的任务就可以恢复登录工作流将用户重定向回登录视图。
      • 当用户遵循的一个LoginPageLink年代,signin查询字符串参数是传递到页面。 这个参数应该作为一个回响signin查询字符串参数到登录页面,当用户想恢复其登录。 login视图位于路径”~ / login”相对于IdentityServer的应用基础。
    • RememberLastUsername
      • 表明IdentityServer是否还记得过去的用户名进入登录页面。 默认为false。
    • IdentityProviders
      • 允许配置额外的身份提供者——看到在这里
    • CookieOptions
      • CookieOptions对象配置如何由IdentityServer饼干。
      • CookieOptions这些属性:
        • Prefix:允许在饼干上设置一个前缀,以避免潜在的冲突与其他饼干使用相同的名称。 默认情况下不使用前缀。
        • ExpireTimeSpan:验证cookie的过期时间。 默认为10个小时。
        • IsPersistent:表明身份验证cookie是否被标记为持久性。 默认为false。
        • SlidingExpiration:表示如果身份验证cookie是滑动,这意味着它自动更新用户是活跃的。 默认为false。
        • Path:设置cookie的道路。 默认为IdentityServer在托管应用程序的基本路径。
        • AllowRememberMe:表示“记住我”选项是否呈现给用户的登录页面。 如果选择这个选项将发行持续验证cookie。 默认为true。
          • 如果这个设置是使用用户的决定(是或否)将覆盖IsPersistent设置。 换句话说,如果两个IsPersistent和AllowRememberMe启用用户决定他们不记得登录,然后不会发行持久化cookie。
          • RememberMeDuration:持久化cookie签发时间登录页面上的“记住我”选项。 默认为30天。
          • SecureMode:获取或设置模式发布安全标志的饼干。 默认为SameAsRequest。
    • EnableSignOutPrompt
      • 表明经过IdentityServer是否会显示一个确认页。 当一个客户端发起一个捲,默认情况下IdentityServer将要求用户确认。 这是一个缓解技术对垃圾邮件“注销”。 默认为true。
    • EnablePostSignOutAutoRedirect
      • 获取或设置一个值,指出是否IdentityServer自动重定向回验证post_logout_redirect_uri传递到signout端点。 默认为false。
    • PostSignOutAutoRedirectDelay
      • 获取或设置重定向到一个之前延迟(以秒为单位)post_logout_redirect_uri。 默认为0。
    • SignInMessageThreshold
      • 获取或设置限制之后,老signin消息(饼干)清除。 默认为5。
    • InvalidSignInRedirectUrl
      • 获取或设置无效登录重定向URL。 如果用户到达登录页面没有一个有效的登录请求,然后他们将被重定向到该URL。 URL必须绝对或相对URL(从“~ /”)。

    v 身份提供者

    IdentityServer支持使用外部身份验证提供者。 外部身份验证机制必须封装在一个武士刀认证中间件。

    武士刀本身附带的中间件为谷歌、FacebookTwitter、微软账户,ws - federationOpenID连接,但也有社区开发中间件)(包括雅虎、LinkedInSAML2p)。 看到这里的选项列表

    为外部提供者配置中间件,添加一个方法接受一个项目IAppBuilder和一个string

    public static void ConfigureIdentityProviders(IAppBuilder app, string signInAsType){

        var google = new GoogleOAuth2AuthenticationOptions

        {

            AuthenticationType = "Google",

            Caption = "Google",

            SignInAsAuthenticationType = signInAsType,

            ClientId = "...",

            ClientSecret = "..."

        };

        app.UseGoogleAuthentication(google);

     

        var fb = new FacebookAuthenticationOptions

        {

            AuthenticationType = "Facebook",

            Caption = "Facebook",

            SignInAsAuthenticationType = signInAsType,

            AppId = "...",

            AppSecret = "..."

        };

        app.UseFacebookAuthentication(fb);

     

        var twitter = new TwitterAuthenticationOptions

        {

            AuthenticationType = "Twitter",

            Caption = "Twitter",

            SignInAsAuthenticationType = signInAsType,

            ConsumerKey = "...",

            ConsumerSecret = "..."

        };

        app.UseTwitterAuthentication(twitter);}

    笔记

    AuthenticationType必须是一个独特的价值来确定外部身份提供商。 这个值也将用于idp在生成的令牌。 而且相同的值可以用着身份提供者在使用授权/身份验证请求acr_values参数(的更多信息)。 这个值也用来限制允许在身份提供者Client配置。

    Caption登录页面上指定的标签按钮身份提供商。 如果Caption是一个空字符串,身份提供者在登录页面将不会显示。 但仍然可以通过登录提示。

    SignInAsAuthenticationType必须设置为我们传递的价值通过吗signInAsType参数

    指定配置方法IdentityProviders财产的AuthenticationOptions:

    var idsrvOptions = new IdentityServerOptions{

        SiteName = "IdentityServer3",

        Factory = factory,

        SigningCertificate = Cert.Load(),

     

        AuthenticationOptions = new AuthenticationOptions 

        {

            IdentityProviders = ConfigureIdentityProviders

        }};

    app.UseIdentityServer(idsrvOptions);

    v 添加ws - federation身份提供者

    基于ws - federation身份提供者可以以相同的方式添加如上所示。

    向后兼容性原因,ws - federation中间件听所有传入请求和检查传入令牌的帖子。 这不是一个问题,如果你只有一个ws - federation中间件配置,但是如果你有不止一个,你需要设置一个显式的和独特的CallbackPath属性相匹配的回复URL配置在国内流离失所者。 请注意,CallbackPath必须相对于根目录,而不是相对于身份服务器模块路径。 例如,如果外部提供者配置为post身份验证令牌http://mydomain.com/SubFolder/IdSrv/MyExternalProvider然后CallbackPath应该设置为/SubFolder/IdSrv/MyExternalProvider

    var adfs = new WsFederationAuthenticationOptions{

        AuthenticationType = "adfs",

        Caption = "ADFS",

        SignInAsAuthenticationType = signInAsType,  MetadataAddress = "https://adfs.leastprivilege.vm/federationmetadata/2007-06/federationmetadata.xml",

     

        Wtrealm = "urn:idsrv3"};app.UseWsFederationAuthentication(adfs);

    v HSTS

    HTTP严格的运输安全(hst)是网络安全的一个重要方面。 IdentityServer3提供了一个配置选项包括hst头在它所有的HTTP响应。 要启用,使用UseHsts扩展方法IAppBuilder在你OWIN配置:

    public void Configuration(IAppBuilder app){

        app.UseHsts();

     

        // ...}

    如果你想设置过期(max-age),然后UseHsts过载,接受吗int的天数或者一个TimeSpan为一个自定义的持续时间。 的值0TimeSpan.Zero可以用来清洗hst浏览器缓存。 默认过期30天。

    v CORS

    许多端点IdentityServer将通过Ajax调用从JavaScript访问。 鉴于IdentityServer很可能比这些客户托管在一个不同的起源,这意味着跨源资源共享(歌珥)将是一个问题。

    v 歌珥政策服务

    IdentityServer3允许托管应用程序来实现ICorsPolicyService确定歌珥政策。 该服务注册的IdentityServerServiceFactory

    单一方法ICorsPolicyService:

    • Task<bool> IsOriginAllowedAsync(string origin)
      • 返回true如果origin是允许的,false否则。

    你可以实现一个自定义的实现在任何你认为合适的方式来确定如果调用起源是被允许的。

    Ø 提供实现

    有两种实现提供从IdentityServer核心:

    • DefaultCorsPolicyService
      • 这个实现可以使用如果允许起源的列表允许是固定的和已知的在应用程序开始。 的AllowedOrigins属性的集合,可以confgured的起源,应该允许列表。
      • 还有一个AllowAll属性可以设置true允许所有的起源。
    • InMemoryCorsPolicyService
      • 这个实现接受作为构造函数参数列表Client对象。 歌珥的起源可以通过配置AllowedCorsOrigins财产的Client对象。

    有一个最后的实现提供了从IdentityServer3.EntityFramework:

    • ClientConfigurationCorsPolicyService
      • 这个实现了其列表允许的起源AllowedCorsOrigins财产的Client对象存储在数据库中。

    v 弃用:CorsPolicy

    在版本1.0.0 IdentityServerCorsPolicy是唯一的方法来支持歌珥和现在已经弃用上述歌珥政策服务。 下面的文档维护,因为1.0.0特性仍然支持,但在未来的版本将被删除。

    Ø CorsPolicy

    IdentityServer3允许托管应用程序配置CorsPolicyIdentityServerOptions控制允许哪些起源。

    AllowedOrigins

    CorsPolicy有两种方法可以指定允许哪些起源。 第一个是AllowedOrigins主机名称的集合。 这是有用的,如果在应用程序启动时间的起源是已知的(硬编码或可能从数据库加载)

    var idsvrOptions = new IdentityServerOptions();

    idsrvOptions.CorsPolicy.AllowedOrigins.Add("http://myclient.com");

    idsrvOptions.CorsPolicy.AllowedOrigins.Add("http://myotherclient.org);

    PolicyCallback

    第二个方法CorsPolicy允许托管应用程序显示允许哪些起源是PolicyCallback代表这是一个Func<string, Task<bool>>。 在运行时调用该委托歌珥请求时被请求IdentityServer并通过原点。 一个返回bool的价值true表明,原点是被允许的。 这是有用的,如果经常更改允许起源列表或者足够大,这样一个数据库查找者优先。

    var idsvrOptions = new IdentityServerOptions();

    idsrvOptions.CorsPolicy.PolicyCallback = async origin =>{

        return await SomeDatabaseCallToCheckIfOriginIsAllowed(origin);};

    CorsPolicy.AllowAll

    为了方便有一个静态属性CorsPolicy.AllowAll这将允许所有的起源。 这是有用的调试或发展。

    var idsvrOptions = new IdentityServerOptions {

        CorsPolicy = CorsPolicy.AllowAll};

    v 日志记录

    IdentityServer产生广泛的日志输出。 日志记录机制,水槽是由通过设定一个托管应用程序LogProvider(例如,在你的startup):

    LogProvider。SetCurrentLogProvider(新 DiagnosticsTraceLogProvider());

    以下供应商的支持:

    l System.Diagnostics。 跟踪(DiagnosticsTraceLogProvider)

    l TraceSource(TraceSourceLogProvider)

    l NLog(NLogLogProvider)

    l 企业图书馆(EntLibLogProvider)

    l SeriLog(SerilogLogProvider)

    l Log4Net(Log4NetLogProvider)

    l 放大镜(LoupeLogProvider)

    您可以创建额外的提供者的推导LogProvider

    v 配置诊断

    的LoggingOptions类具有以下设置:

    • EnableWebApiDiagnostics
      • 如果启用,Web API内部诊断日志记录将被转发到日志提供者
    • WebApiDiagnosticsIsVerbose
      • 如果启用,Web API诊断日志记录将被设置为详细
    • EnableHttpLogging
      • 如果启用,HTTP请求和响应将被记录
    • IncludeSensitiveDataInLogs
      • 如果启用,标准的日志可能包含敏感数据,如PII数据

    警告 EnableHttpLogging可能的冲突与其他框架加载到相同的web应用程序(它肯定与MVC)。 您不能使用HTTP记录在这种情况下。

    v 配置系统 诊断提供者

    以下代码片段添加到您的配置文件将所有日志消息到一个简单的文本文件。 我们使用Baretail查看日志文件。

    注意:如果你使用这种方法你需要确保账户运行应用程序池包含日志文件的目录有写访问权限。 如果你不指定一个路径,这将是应用程序目录,不建议生产场景。 生产记录到一个文件外应用程序目录。

    <system.diagnostics>

      <trace autoflush="true"

             indentsize="4">

        <listeners>

          <add name="myListener"

               type="System.Diagnostics.TextWriterTraceListener"

               initializeData="Trace.log" />

          <remove name="Default" />

        </listeners>

      </trace></system.diagnostics>

     

     

    v 配置TraceSource提供者

    以下代码片段添加到您的配置文件。 您可以使用服务跟踪查看器的工具。 净SDK检查跟踪文件。

    <sources>

      <source name="Thinktecture.IdentityServer"

              switchValue="Information, ActivityTracing">

        <listeners>

          <add name="xml"

                type="System.Diagnostics.XmlWriterTraceListener"

                initializeData= "trace.svclog" />

        </listeners>

      </source></sources>

    v 插装自己的代码

    您还可以使用日志系统在您自己的代码的可扩展性。

    添加一个静态ILog实例类

    private readonly static ILog Logger = LogProvider.GetCurrentClassLogger();

    使用日志记录器记录你的消息

    Logger.Debug("Getting claims for identity token");

    v 事件

    IdentityServer在运行时引发了一系列的事件,:

    l 成功/失败身份验证(资源所有者流,pre、片面、本地和外部)

    l 令牌发出(身份、访问刷新令牌)

    l 令牌处理相关事件(授权代码刷新令牌发出/赎回/刷新)

    l 许可撤销

    l 端点成功/失败

    l 过期无效/没有签名证书

    l 未处理的异常和内部错误

    l CSP错误报告的浏览器。 看到CSP为更多的信息。

    默认情况下这些事件转发到日志提供者——配置 一个定制的事件服务可以处理或以任何方式提出适合环境。

    v 配置事件

    的EventsOptions类具有以下设置(默认false):

    • RaiseSuccessEvents
      • 如刷新刷新令牌或身份验证成功
    • RaiseFailureEvents
      • 如身份验证失败,授权代码救赎失败
    • RaiseErrorEvents
      • 如未处理的异常
    • RaiseInformationEvents
      • 如令牌有效发行或证书

    第六章端点

    v 授权/身份验证端点

    授权端点可以用来请求访问令牌或授权码(隐式和授权代码流)。 你可以使用一个web浏览器或web视图开始这个过程。

    Ø 支持参数

    看到规范

    • client_id(必需)
      • 客户机的标识符
    • scope(必需)
      • 一个或多个注册范围
    • redirect_uri(必需)
      • 必须精确匹配的一个允许重定向uri的客户吗
    • response_type(必需)
      • code请求一个授权代码
      • token请求一个访问令牌(只允许资源范围)
      • id_token token请求一个身份令牌和一个访问令牌(包括资源和身份允许范围)
    • response_mode(可选)
      • form_post发送令牌响应作为后一种形式,而不是一个片段编码的重定向
    • state(推荐)
      • idsrv将回声状态值标记的回应,这是对于关联请求和响应
    • nonce(所需的身份令牌使用隐式流)
      • idsrv将回声的nonce价值身份令牌,这是关联的令牌请求)
    • prompt(可选)
      • none没有用户界面将显示在请求。 如果这是不可能的(例如,因为用户必须登录或同意)将返回一个错误
      • login登录界面将显示,即使用户拥有一个有效的会话
    • login_hint(可选)
      • 可用于预填写登录页面上的用户名字段
    • max_age(可选)
      • 如果用户的登录会话超过最大年龄(以秒为单位),登录界面将显示
    • acr_values(可选)
      • 允许通过额外的身份验证相关信息用户服务——还有价值观具有特殊的意义:
        • idp:name_of_idp绕过登录屏幕/家庭领域,并将用户直接转发给所选身份提供商每客户端配置(如果允许)
        • tenant:name_of_tenant可以用来传递一个租户名称的用户服务

    Ø 例子

    可读性(URL编码删除)

    GET /connect/authorize?client_id=client1&scope=openid email api1&response_type=id_token token&redirect_uri=https://myapp/callback&state=abc&nonce=xyz

    v 令牌端点

    令牌端点可以用于以编程方式请求或刷新令牌(资源所有者凭证流密码,授权代码流,客户端凭证流和自定义授权类型)。

    Ø 支持参数

    看到规范

    • grant_type(必需)
      • authorization_code, client_credentials, password, refresh_token或自定义
    • scope(所需的所有授权类型除了refresh_token和代码)
    • redirect_uri(所需代码授予类型)
    • code(所需代码授予)
    • username格兰特(所需的密码类型)
    • password(密码grant_type所需)
    • acr_values(允许密码授权类型将额外的信息传递给用户的服务)
      • 有值和特殊的意义:
        • idp:name_of_idp绕过登录屏幕/家庭领域,并将用户直接转发给所选身份提供商每客户端配置(如果允许)
        • tenant:name_of_tenant可以使用额外的信息传递给用户服务
    • refresh_token(需要刷新令牌授权)
    • client_id(后的身体,或作为一个基本身份验证头)
    • client_secret(后的身体,或作为一个基本身份验证头)

    Ø 身份验证

    所有请求令牌端点必须经过身份验证——要么通过基本身份验证通过客户机id和秘密 或添加client_idclient_secret字段的身体。

    当提供client_idclient_secretAuthorization头预计:

    l client_id:client_secret

    l Base64编码的

    var clientId = "...";

    var clientSecret = "...";

    var encoding = Encoding.UTF8;

    var credentials = string.Format("{0}:{1}", clientId, clientSecret);

    var headerValue = Convert.ToBase64String(encoding.GetBytes(credentials));

    Ø 例子

    (Form-encoding删除和添加换行符是为了增加可读性)

    POST /connect/token

    Authorization: Basic abcxyz

     

    grant_type=authorization_code&

    code=hdh922&

    redirect_uri=https://myapp.com/callback

    v 用户信息端点

    UserInfo端点可以用来获取身份信息主题。 它需要一个有效的访问令牌至少openid的范围。

    看到规范

    Ø 例子

    GET /connect/userinfo

    Authorization: Bearer <access_token>

    HTTP/1.1 200 OK

    Content-Type: application/json

     

    {

       "sub": "248289761001",

       "name": "Bob Smith",

       "given_name": "Bob",

       "family_name": "Smith",

       "role": [

           "user",

           "admin"

       ]

    }

    v 发现端点

    发现端点可以用来检索关于IdentityServer——它返回元数据信息,如发行人名称、关键材料,支持范围等。

    看到规范

    Ø 例子

    GET /.well-known/openid-configuration

    v 注销端点

    重定向到注销端点扫清了身份验证会话和饼干。

    您可以通过以下的端点可选参数:

    • id_token_hint
      • 客户机的id_token期间获得认证。 这允许绕过注销确认屏幕以及提供一篇注销重定向URL
    • post_logout_redirect_uri
      • 一个URI,IdentityServer可以重定向到后注销(默认情况下显示一个链接)。 URI必须允许注销后URI列表中的客户端。

    /connect/endsession?id_token_hint=...&post_logout_redirect_uri=https://myapp.com

    看到AuthenticationOptions

    v 令牌撤销

    这个端点允许撤销访问令牌(仅参考标记)和刷新令牌。 它实现了令牌撤销规范(RFC 7009)

    支持参数:

    • token(必需)
      • 令牌撤销
    • token_type_hint
      • 要么access_token或refresh_token

    使用一个支持的请求必须经过身份验证的客户端身份验证方法。

    例子:

    POST /connect/revocation HTTP/1.1

    Host: server.example.com

    Content-Type: application/x-www-form-urlencoded

    Authorization: Basic czZCaGRSa3F0MzpnWDFmQmF0M2JW

     

    token=45ghiukldjahdnhzdauz&token_type_hint=refresh_token

    v 访问令牌验证端点

    访问令牌验证端点可以用来验证参考标记。 它也可以用来验证独立JWT如果消费者没有支持适当的JWT或加密库。

    Ø 例子

    GET /connect/accesstokenvalidation?token=<token>

    一个成功的响应将返回一个状态码200和相关申请令牌。 一次不成功的响应将返回一个400错误消息。

    还可以通过一个范围,预计将在令牌:

    GET /connect/accesstokenvalidation?token=<token>&expectedScope=calendar

    v 身份令牌验证端点

    身份令牌验证端点可以用来验证身份令牌。 这是有用的为客户,无法获得适当的JWT或加密库(例如JavaScript)

    Ø 例子

    GET /connect/identitytokenvalidation?token=<token>&client_id=<expected_client_id>

    一个成功的响应将返回一个状态码200和相关申请令牌。 一次不成功的响应将返回一个400错误消息。

    {

      "nonce": "nonce",

      "iat": "1413203421",

      "sub": "88421113",

      "amr": "password",

      "auth_time": "1413203419",

      "idp": "idsrv",

      "iss": "https://idsrv3.com",

      "aud": "implicitclient",

      "exp": "1413203781",

      "nbf": "1413203421"

    }

    v CSP端点

    CSP允许端点配置报告。 IdentityServer CSP的错误记录浏览器提供一个端点报告。 这些CSP错误将提高事件在事件系统。

    CSP报告特性可以通过设置禁用EnableCspReportEndpoint财产falseEndpointOptions的一个属性是什么IdentityServerOptions

    第七章先进的

    v 刷新令牌

    l 刷新令牌支持以下流:授权代码,混合动力和资源所有者凭证流密码。

    l 客户需要允许请求offline_access范围刷新令牌。

    Ø 设置在客户端类

    • RefreshTokenUsage
      • 重用:刷新令牌处理刷新令牌时将保持不变
      • 前:刷新令牌处理刷新令牌时将被更新
    • RefreshTokenExpiration
      • 绝对:刷新令牌将到期在一个固定的时间点(AbsoluteRefreshTokenLifetime指定的)
      • 滑动:刷新令牌时,将再次刷新令牌的生命周期(通过SlidingRefreshTokenLifetime中指定的数量)。 绝对不会超过生命一生。
    • AbsoluteRefreshTokenLifetime
      • 一生最大的刷新令牌在几秒钟内。 默认为2592000秒/ 30天
    • SlidingRefreshTokenLifetime
      • 滑动的一生秒刷新令牌。 默认为1296000秒/ 15天

    Ø 使用

    l 请求offline_access范围(通过代码或资源所有者流)

    l 使用刷新令牌refresh_token格兰特

    Ø 样品

    l 客户端样本既有资源所有者代码流

    v 定制服务

    IdentityServer3提供了许多扩展点用于存储数据,验证逻辑和通用功能 支持IdentityServer所需的操作作为一个令牌服务。 这些不同的扩展点统称为服务

    看到在这里服务的完整列表。

    v 强制性的服务

    三是强制性的,必须配置的服务实现者,他们是: 

    l 用户服务(IUserService) 

    l 客户端存储(IClientStore) 

    l 存储(范围IScopeStore) 

    我们提供一个简单的内存版本的这三个服务以及支持通过回购或社区项目相关的数据存储。 看到在这里为更多的细节。

    v 注册定制服务

    你可以取代所有服务和注册额外定义的。 这是封装的Registration类。 一个Registration代表一种IdentityServer获得您的服务的一个实例。

    根据您的服务的设计您可能想要对每个请求一个新实例,使用单例或者你可能需要特殊的实例化逻辑每次实例是必要的。 为了适应这些不同的可能性,Registration类提供了许多不同的构造函数来注册服务:

    • new Registration<T>(Type yourImplementation)
      • 寄存器yourImplementation当类实现T接口。
    • new Registration<T, Impl>()
      • 寄存器Impl当类实现T接口。 这个API是一个简单方便的接受的前面Type参数。
    • new Registration<T>(T singleton)
      • 注册singleton作为一个单例实现传递的实例T接口。
    • new Registration<T>(Func<IDependencyResolver, T> factory)
      • 注册一个回调函数,将调用返回的实现T接口。

    var factory = new IdentityServerServiceFactory();factory.UserService = new Registration<IUserService, MyCustomUserService>();

    看到这个页面依赖注入(DI)页面了解更多细节。

    Ø 服务清理

    在所有情况下,除了单,如果您的服务实现IDisposable,然后Dispose

    第八章依赖注入

    这个话题可以找到的样本在这里

    IdentityServer3已经为各种服务扩展点。 默认的实现这些服务被设计为与其他IdentityServer移动部件 ,因此我们使用依赖注入一切连接起来。

    v 注入IdentityServer服务

    默认提供的服务可以取代IdentityServer托管应用程序(如果需要的话)。 自定义实现也可以使用依赖项注入和IdentityServer类型甚至定制类型注射。 这仅仅是支持自定义服务和商店注册使用Registration

    定制服务接受IdentityServer定义的类型,只是表明这些依赖项作为构造函数参数。 当IdentityServer实例化你的注册类型构造函数的参数将被自动解决。 例如:

    public class MyCustomTokenSigningService: ITokenSigningService{

        private readonly IdentityServerOptions _options;

     

        public MyCustomTokenSigningService(IdentityServerOptions options)

        {

            _options = options;

        }

     

        public Task<string> SignTokenAsync(Token token)

        {

            // ...

        }}

    public class MyCustomTokenSigningService: ITokenSigningService{

        private readonly IdentityServerOptions _options;

     

        public MyCustomTokenSigningService(IdentityServerOptions options)

        {

            _options = options;

        }

     

        public Task<string> SignTokenAsync(Token token)

        {

            // ...

        }}

    这是注册为这样:

    var factory = new IdentityServerServiceFactory();factory.TokenSigningService = new Registration<ITokenSigningService>(typeof(MyCustomTokenSigningService));

    或者用这个语法:

    var factory = new IdentityServerServiceFactory();factory.TokenSigningService = new Registration<ITokenSigningService, MyCustomTokenSigningService>();

    v 注入定制服务

    定制服务也可能依赖于你自己的类型。 这些也可以注入只要他们已经配置了IdentityServer的依赖注入系统。 这是通过添加新注册使用IdentityServerServiceFactoryRegister()方法。 例如,如果你需要定制日志记录器服务:

    public interface ICustomLogger{

        void Log(string message);}

    public class DebugLogger : ICustomLogger{

        public void Log(string message)

        {

            Debug.WriteLine(message);

        }}

    public class MyCustomTokenSigningService: ITokenSigningService{

        private readonly IdentityServerOptions _options;

        private readonly ICustomLogger _logger;

     

        public MyCustomTokenSigningService(IdentityServerOptions options, ICustomLogger logger)

        {

            _options = options;

            _logger = logger;

        }

     

        public Task<string> SignTokenAsync(Token token)

        {

            // ...

        }}

    然后它将被登记为这样:

    var factory = new IdentityServerServiceFactory();factory.TokenSigningService = new Registration<ITokenSigningService, MyCustomTokenSigningService>();factory.Register(new Registration<ICustomLogger, MyCustomDebugLogger>());

    Ø 没有一个接口定制服务

    在上面的例子中注入类型是ICustomLoggerimpelemntationMyCustomDebugLogger。 如果没有设计定制服务接口实现单独的合同,然后注册的具体类型本身可以被注入。

    例如,如果MyCustomTokenSigningService的构造函数不接受记录器的接口,因此:

    public class MyCustomTokenSigningService: ITokenSigningService{

        public MyCustomTokenSigningService(IdentityServerOptions options, MyCustomDebugLogger logger)

        {

            _options = options;

            _logger = logger;

        }

     

        // ...}

    然后registraion可配置是这样的:

    var factory = new IdentityServerServiceFactory();

    factory.TokenSigningService = new Registration<ITokenSigningService,

     MyCustomTokenSigningService>();

    factory.Register(new Registration<MyCustomDebugLogger>());

    简而言之,这种类型的注册意味着MyCustomDebugLogger具体类型是被注入依赖的类型。

    v 创建自定义

    如果你的服务是必要的人工构造(如您需要传递特定参数的构造函数),然后您可以使用Registration,允许一个工厂要使用回调。 这Registration这个签名:

    new Registration<T>(Func<IDependencyResolver, T> factory) 

    返回值必须的一个实例T接口和一个例子可能是这个样子:

    var factory = new IdentityServerServiceFactory();

    factory.TokenSigningService = new Registration<ITokenSigningService>(resolver =>

        new MyCustomTokenSigningService("SomeSigningKeyValue"));

    Ø 获得其他依赖项

    虽然这种方法允许您最灵活地创建你的服务,你还可能需要使用其他服务。 这就是IDependencyResolver用于。 它允许您从在你的回调函数得到服务。 例如,如果您的自定义客户端存储需要依赖于另一个服务在IdentityServer,它可以是这样获得的:

    var factory = new IdentityServerServiceFactory();

    factory.TokenSigningService = new Registration<ITokenSigningService>(resolver =>

        new MyCustomTokenSigningService("SomeSigningKeyValue",

     resolver.Resolve<ICustomLogger>()));

    Ø 叫依赖

    最后,当通过注册自定义依赖关系IdentityServerServiceFactoryRegister()方法,可以命名的依赖关系。 这个名字是表示通过name构造函数的参数Registration类。 这仅仅是用于自定义注册和名称只能解决依赖关系时使用IDependencyResolver在自定义工厂回调。

    命名的依赖可能是有用的注册相同的多个实例T提供一种机制来实现所需的。 例如:

    string mode = "debug"; // or "release", for example

    var factory = new IdentityServerServiceFactory();

    factory.Register(new Registration<ICustomLogger,MyFileSystemLogger>("debug"));

    factory.Register(new Registration<ICustomLogger, MyDatabaseLogger>("release"));

    factory.TokenSigningService = new Registration<ITokenSigningService>(resolver =>

        new MyCustomTokenSigningService(resolver.Resolve<ICustomLogger>(mode)));

    在这个例子中,mode作为配置国旗impelmentation的影响ICustomLogger在运行时将使用。

    v 实例化与登记模式

    Registration类允许服务显示多少的实例将创建的服务。 的Mode属性是一个枚举这些可能的值:

    • InstancePerHttpRequest
      • 每个HTTP请求将创建一个实例。 这意味着,如果在单个服务请求两次依赖链则将共享相同的实例。
    • InstancePerUse
      • 将创建一个实例/位置服务是必要的。 这意味着,如果在单个服务请求两次依赖链然后将创建两个单独的实例。
    • Singleton
      • 只有一个实例将被创建。 当使用这种模式是自动分配Registration接受一个单例实例作为构造函数参数。

    v 客户端缓存结果、范围和用户存储

    有各种各样的商店允许IdentityServer从数据库加载数据。 鉴于IdentityServer的一般解耦设计,可以多次调用api加载数据IdentityServer相同的HTTP请求。 这可能招致不必要的往返数据库。 考虑到这种可能性,IdentityServer定义了一个缓存接口,这样您就可以实现自己的缓存逻辑。 此外,IdentityServer提供了一个默认的缓存实现。

    v 默认的缓存

    IdentityServer提供的默认缓存是一个内存中的缓存和是可配置的缓存数据的时间到期。 这个默认缓存(虽然很简单)应该解决性能问题。 配置这个默认缓存,上有扩展方法IdentityServerServiceFactory:

    l ConfigureClientStoreCache

    l ConfigureScopeStoreCache

    l ConfigureUserServiceCache

    这些是重载要么不接受参数(使用默认的期满5分钟),或一个TimeSpan表明一个定制的到期值。 这些api使用这样的:

    var factory = InMemoryFactory.Create(

        users:   Users.Get(),

        clients: Clients.Get(),

        scopes:  Scopes.Get());

     

    factory.ConfigureClientStoreCache();

    factory.ConfigureScopeStoreCache();

    factory.ConfigureUserServiceCache(TimeSpan.FromMinutes(1));

    这些方法应该被称为后分配到合适的商店IdentityServerServiceFactory,因为他们使用装饰模式包装实际的存储实现。

    v 自定义缓存

    如果需要自定义缓存实现(例如使用复述,),那么您可以实现的ICache<T>需要缓存的数据。 缓存接口定义了这个API:

    • Task SetAsync(string key, T item)
      • 的key和item缓存。
    • Task<T> GetAsync(string key)
      • 的key表明项目从缓存中访问。 一个null返回条目显示在缓存中没有条目。

    IdentityServerServiceFactory上面描述的扩展方法也超载也接受Registration自定义缓存:

    l ConfigureClientStoreCache(Registration<ICache<Client>> cacheRegistration)

    l ConfigureScopeStoreCache(Registration<ICache<IEnumerable<Scope>>> cacheRegistration)

    l ConfigureUserServiceCache(Registration<ICache<IEnumerable<Claim>>> cacheRegistration)

    v 自定义视图

    IdentityServer3向用户显示不同的观点。 IdentityServer视图需要登录,注销提示,退出系统,同意,客户端权限和错误。 这些观点仅仅是web页面显示在浏览器中。 获得这些视图的标记,IdentityServer定义了IViewService接口。 视图服务是IdentityServer的一个可选的扩展点。

    Ø 默认视图服务

    可以找到这个话题的样本在这里

    视图的默认实现IdentityServer是所使用的服务DefaultViewService。 各种资产(HTMLJavaScriptCSS和字体)组成的观点从嵌入式资源在IdentityServer组装。

    DefaultViewService允许一定数量的定制。

    CSS和脚本定制

    定制的一个简单的方法是托管的应用程序可以提供一个列表的CSSJavaScript文件包含在默认的web页面。 这允许品牌而不需要完全取代资产本身。

    DefaultViewServiceOptions类是用来表示这些CSSJavaScript文件通过StylesheetsScripts列表:

    var viewOptions = new DefaultViewServiceOptions();

    viewOptions.Stylesheets.Add("/Content/Site.css");

    的路径传递给Add可以是相对于IdentityServer的基本路径前缀路径的“~”(“~ /道路/ file.css”),或者可以通过加前缀host-relative路径的路径用“/”(“/道路/ file.css”)。 绝对url添加支持,只要他们IdentityServerCSP的选项

    然后使用DefaultViewServiceOptions有一个ConfigureDefaultViewService扩展方法IdentityServerServiceFactory。 这个助手方法使用依赖注入系统注册DefaultViewService及其依赖项的基础上选择:

    var viewOptions = new DefaultViewServiceOptions();

    viewOptions.Stylesheets.Add("/Content/Site.css"); 

    var factory = new IdentityServerServiceFactory();

    factory.ConfigureDefaultViewService(viewOptions);

    HTML定制

    替换整个视图

    DefaultViewService允许HTML的定制。 默认视图可以通过创建HTML文件在一个覆盖”assets托管应用程序库中的文件夹目录。 文件包含在所在地的名字匹配正在呈现的视图(例如login视图将寻找一个文件命名login.html)。 如果这些文件存在,那么他们负责呈现的HTML视图的全部。

    替换部分的观点

    呈现其默认视图时,DefaultViewService使用模板机制(类似于MVC)布局模板。 有一个共享的布局视图,还有单独的部分的观点(一个为每个页面需要显示)conatin里面的内容呈现的布局。 当一个视图呈现这两个合并在一起在服务器发出一个HTML文档。

    这一点本讨论的是当自定义HTML,而不是取代整个HTML文档可以取代部分视图。 替换的部分视图文件位于assets文件夹只需要杰出的在名字前面加上与underscope(如。_login.html)。 这将会使用默认的布局模板,但使用自定义局部视图。 它将被合并到布局模板合并后的HTML呈现到浏览器。

    除了能够取代部分视图,也可以替换默认布局模板本身。 这可以通过创建一个命名的文件_layout.htmlassets文件夹中。 的DefaultViewService将使用任何组合的自定义布局或部分观点发现与默认的嵌入式文件系统合并资产呈现请求的视图。

    缓存

    自定义视图将默认缓存的内存,所以如果文件被改变了,那么它将需要一个应用程序重新启动加载任何更新的HTML。 这种行为可以通过设置被禁用CacheViews财产falseDefaultViewServiceOptions前面所述。

    自定义视图加载程序

    最后,如果assets文件系统上的文件夹不可取,那么您可以实现您自己的自定义存储视图通过implmenetingIViewLoader接口。 这是作为一个配置Registration<IViewLoader>DefaultViewServiceOptions

    Ø 自定义视图服务

    可以找到这个话题的样本在这里

    如果托管应用程序需要完全控制视图(HTMLCSSJavaScript)就可以实现的IViewService控制的所有标记呈现视图。 自定义视图服务将被注册ViewService财产的IdentityServerServiceFactory

    的方法IViewService每个预计将产生一个接口Stream包含UTF8编码标记显示的各种视图(登录、同意等)。 这些方法都接受作为参数模型特定的视图(如呈现LoginViewModel,或者一个ConsentViewModel)。 这个模型提供了上下文信息,很可能需要向用户提供(例如客户端和范围在同意的观点,或错误消息错误观点,URL提交凭证登录)

    大多数视图将需要请求回到IdentityServer内不同的端点。 这些GETPOST请求必须包含相同的输入,默认视图发送到服务器。 url中包含的各种模型。

    反跨站点请求伪造(Anti-XSRF)

    后回服务器的视图必须包括一个form-URL-encoded anti-xsrf令牌的请求主体。 每个包含一个相关的模型AntiForgeryTokenViewModel对象的NameValue预计的参数。 自定义视图必须包括这些当提交用户的输入数据。

    v 本地化的消息

    消息IdentityServer创建可以通过实施本地化(或简单地取代)ILocalizationService接口。 它定义了一个API:

    l string GetString(string category, string id)

    这个API是通过了category的消息。 IdentityServer只定义了三个类:

    public static class LocalizationCategories

    {

        public const string Messages = "Messages";

        public const string Events = "Events";

        public const string Scopes = "Scopes";

    }

    id参数显示类别的特定信息。 各种标识符,常量定义的使用IdentityServer3.Core.Resources名称空间。 举例来说,这里是一个简短的片段的常量(咨询完整列表的代码):

    namespace IdentityServer3.Core.Resources

    {

        public class MessageIds

        {

                public const string ClientIdRequired = "ClientIdRequired";

                public const string ExternalProviderError = "ExternalProviderError";

                public const string Invalid_scope = "Invalid_scope";

                public const string InvalidUsernameOrPassword = 

                                                           "InvalidUsernameOrPassword";

                public const string MissingClientId = "MissingClientId";

                // ...

      }

    }

    默认的实现ILocalizationService

    v CSP

    IdentityServer合并使用内容安全政策(CSP)的所有HTML页面显示。

    Ø CspOptions

    IdentityServer3允许托管应用程序配置CspOptionsIdentityServerOptions控制CSP的行为。 以下是可配置的设置:

    l Enabled:指示是否启用或禁用CSP。 默认为true

    l ScriptSrc:允许额外的script-src值被添加到默认策略。

    l StyleSrc:允许额外的style-src值被添加到默认策略。

    l FontSrc:允许额外的font-src值被添加到默认策略。

    l ConnectSrc:允许额外的connect-src值被添加到默认策略。

    CSP允许端点配置报告。 端点描述IdentityServer CSP提供了一个报告在这里

    v 用户服务

    这个话题可以找到的样本在这里

    IdentityServer3定义了IUserService接口抽象底层身份管理系统用于用户。 它为用户提供了语义验证与本地账户和外部账户。 它还提供了身份和声称IdentityServer标记和用户所需信息端点。 此外,用户可以控制服务工作流在登录时用户体验。

    IUserService接口定义了这些方法:

    • AuthenticateLocalAsync
      • 此方法要求本地身份验证(当用户使用用户名和密码的对话框)。
    • AuthenticateExternalAsync
      • 这个方法被调用时,用户使用一个外部身份验证提供者。
    • PreAuthenticateAsync
      • 这个方法被调用之前登录页面。 这允许您确定用户应该由一些乐队的身份验证机制(例如客户端证书或信任的头)。
    • GetProfileDataAsync
      • 调用此方法时,关于用户要求(如令牌创建期间或通过用户信息端点)。
    • IsActiveAsync
      • 这个方法被调用时服务器需要确定用户身份仍然被认为是有效的或活动(例如,如果用户的帐户已停用,因为他们登录)。
    • SignOutAsync
      • 这个方法被调用,当用户信号。

    v 身份验证

    三个的apiIUserService模型验证:AuthenticateLocalAsync,AuthenticateExternalAsync,PreAuthenticateAsync。 所有这三个api传递SignInMessage对象代表请求登录上下文信息。 这些上下文属性通常从客户机传递到授权端点

    SignInMessage包含这些属性可能感兴趣的用户服务。

    l ClientId:客户端请求登录标识符。

    l IdP:外部身份提供商要求。

    l Tenant:租户用户预计将来自。

    l LoginHint:预期的用户名用户将使用登录。

    l DisplayMode:显示模式从授权请求。

    l UiLocales:UI地区从授权请求。

    l AcrValues:acr值通过授权请求。

    除了SignInMessage,一些身份验证方法接受额外的参数。

    AuthenticateLocalAsync如果用户输入凭据到本地调用IdentityServer登录页面。 用户名和密码参数,以及SignInMessage

    AuthenticateExternalAsync当外部调用身份提供商是用于验证。 的ExternalIdentity是通过了ExternalIdentity,以及SignInMessage

    ExternalIdentity包含:

    l Provider:外部身份提供者标识符。

    l ProviderId:用户的唯一标识符提供的外部身份提供商。

    l Claims:要求为用户提供从外部身份提供者。

    PreAuthenticateAsync调用之前显示登录页面,允许用户自定义服务,防止登录页面显示。 这通常是由于一些外部参数,通知用户服务,用户应该已经登录。 只有通过了SignInMessage

    Ø 验证结果

    所有的认证方法的返回值是一个AuthenticateResult。 返回的AuthenticateResult表明许多可能的结果之一。 使用构造函数重载,表明这些结果的选择。 结果是:

    完整的登录

    完全用户登录身份验证API必须产生一个subject和一个name代表用户。 的subject是用户服务的用户和惟一标识符name是一个显示的名称 用户将显示在用户界面。

    可选列表Claim也可以提供。 这些说法可以是任何额外的值,用户可能需要在其他api服务对用户的服务。

    如果用户正在登录,他们使用一个外部身份提供者,那么identityProvider参数也应表示。 这将是包括的idp声称的身份令牌和用户信息端点。 如果用户正在验证本地帐户,然后不应使用该参数(默认的Constants.BuiltInIdentityProvider而不是使用)

    还有一个可选的authenticationMethod参数的填充amrClaims。 这可以用来表示用户身份验证,如两因素身份验证,或客户端证书。 如果不通过,password假定为本地登录。 对于外部登录,然后的价值external将自动用于指示一个外部身份验证。

    这些说法的完整(subject, name, idp, amr和列表Claim)是用于填充为用户发出的认证cookie IdentityServer。 这种身份验证cookie发行和管理通过使用武士刀cookie验证中间件的AuthenticationType的常数Constants.PrimaryAuthenticationType

    ClaimsPrincipal从创建完整的登录然后用作参数的其他apiIUserService。 这些apiGetProfileDataAsync, IsActiveAsync,SignOutAsync

    部分登录(重定向)

    除了一个完整的登录,身份验证api可以执行部分登录。 部分登录表明用户已经证明他们的身份,有本地帐户,但尚未允许继续。 他们必须首先执行其他操作之前或提供其他数据完全被允许登录。 这是有用的定制用户的工作流之前让他们完全登录。 这可能是有用的,迫使用户填写注册页面,改变密码,或者接受EULA之前让他们继续下去。

    这部分执行登录通过发行使用武士刀的部分登录饼干饼干中间件的身份验证AuthenticationType的常数Constants.PartialSignInAuthenticationType

    部分的登录显示所有相同的参数如上所述登录(即为一个完整的。subject, name, claims, amr,idp)以及redirectPath。 的redirectPath代表了一个自定义web页面提供的托管应用程序,用户将被重定向到。 在用户的web页面subjectname声称可以用来识别用户(除了所有其他Claims表示)。 为了获得这些说法,页面必须使用武士刀验证中间件来验证Constants.PartialSignInAuthenticationType身份验证类型,或仅仅是执行在同一个饼干路径IdentityServerweb服务器。

    一旦用户自定义web页面已经完成他们的工作,他们可以被重定向回IdentityServer继续登录过程。 提供的URL将用户重定向回的ClaimsConstants.PartialSignInAuthenticationType身份验证类型并确定的要求类型Constants.ClaimTypes.PartialLoginReturnUrl

    外部登录(重定向)

    可能用户执行外部认证,但是没有为用户本地帐户。 一个定制的用户服务可以选择重定向用户没有本地帐户。 这是通过创建一个执行AuthenticateResult与重定向ExternalIdentity传递到AuthenticateExternalAsyncAPI。 这个执行部分登录(如通过以上PartialSignInAuthenticationType),但没有在发布了饼干。 取而代之的是Claims的类型external_provider_user_id(或通过Constants.ClaimTypes.ExternalProviderUserId),Issuer是外部提供者标识符的值是外部提供者的用户标识符。 这些值可以用于创建一个本地帐户并将外部账户相关联。

    一旦用户完成注册自定义web页面,就可以重定向回IdentityServer通过相同的PartialLoginReturnUrl如上所述。

    登录错误

    最后,验证api可以提供一个错误,将会显示在登录视图。 这是表示通过创建AuthenticateResult使用构造函数,它接受一个string(错误)作为其参数。

     

     

    第九章部署

    请注意本文档是正在进行的工作。 随时添加新的内容。

    v 数据保护

    如果您正在运行在IIS,您需要同步机键。 如果您正在运行IIS之外您需要使用武士刀的web农场兼容的数据保护。

    不幸的是,刀不附带一个开箱即用的。 IdentityServer包括一个数据保护的基础上 X。 509证书(X509CertificateDataProtector),您可以设置的选项类。

    v 终止SSL

    如果你想终止SSL负载均衡器,有两个相关的设置选项:

    • RequireSsl
      • 设置为false,允许非ssl负载均衡器和IdentityServer之间的连接。
    • PublicOrigin
      • 因为你的农场内部节点比公共可访问的地址有不同的名称,IdentityServer不能使用它 链接的一代。 向公众洞穴这个属性的名字。

    v 签名密钥

    确保签名证书部署到所有节点。

    v 配置数据

    作用域的配置数据,客户和用户必须同步。

    他们要么是静态的,你改变通过持续部署配置数据,或者你使用持久层 实体框架回购(MongoDB或社区的贡献)

    v 操作数据

    一些功能需要操作数据的共享数据库,即授权代码,参考令牌和刷新令牌。 如果你使用这些功能需要一个持久层。 你可以使用我们implemenation实体框架。

    v 缓存

    IdentityServer有一个简单的内置内存缓存。 这是有用的但不像它可以优化网络农场。 你可以插入自己的缓存实现ICache

    第十章实体框架支持客户、范围和操作数据

    概述

    支持实体框架IdentityServer来源回购。

    v 配置客户机和范围

    如果范围或客户数据需要从数据库加载(而不是使用内存配置),然后提供一个基于实体框架的实现IClientStoreIScopeStore服务。更多的信息

    v 操作数据

    另外,强烈建议操作数据(如授权码,刷新令牌,参考标记,和用户同意)被保存在数据库中。 因此,我们也有实体框架的实现IAuthorizationCodeStore, ITokenHandleStore, IRefreshTokenStore,IConsentStore服务。更多的信息

    v 客户和范围

    v 商店

    Ø ClientStore

    ClientStoreEF-based实现吗IClientStore接口。 它可以单独使用的ScopeStore

    Ø ScopeStore

    ScopeStoreEF-based实现吗IScopeStore接口。 它可以单独使用的ClientStore

    v 登记

    使用的商店,他们需要注册。 上有扩展方法IdentityServerServiceFactory允许商店都进行配置。 所有的扩展方法接受一个EntityFrameworkServiceOptions它包含这些属性:

    l ConnectionString:配置连接字符串的名字.config文件。

    l Schema:一个可选的数据库模式使用的表。 如果没有提供,则将使用默认数据库。

    配置独立商店,这段代码可以使用:

    var efConfig = new EntityFrameworkServiceOptions {

       ConnectionString = "SomeConnectionName",

       //Schema = "someSchemaIfDesired"};

    var factory = new IdentityServerServiceFactory();

    factory.RegisterClientStore(efConfig);

    factory.RegisterScopeStore(efConfig);

    如果两个商店将使用相同的EntityFrameworkServiceOptions,然后提供一个方便的扩展方法:

    var efConfig = new EntityFrameworkServiceOptions {

       ConnectionString = "SomeConnectionName",

       //Schema = "someSchemaIfDesired"};

    var factory = new IdentityServerServiceFactory();

    factory.RegisterConfigurationServices(efConfig);

    第十一章操作数据

    IdentityServer3的许多特性的数据库需要坚持各种运营数据。 这包括授权码,刷新令牌,参考标记和用户同意。

    登记

    有各种各样的商店将持久化操作数据。 这些都是配置了一个扩展方法IdentityServerServiceFactory。 所有的扩展方法接受一个EntityFrameworkServiceOptions它包含这些属性:

    l ConnectionString:配置连接字符串的名字.config文件。

    l Schema:一个可选的数据库模式使用的表。 如果没有提供,则将使用默认数据库。

    配置操作数据存储可以使用这段代码:

    var efConfig = new EntityFrameworkServiceOptions {

       ConnectionString = "SomeConnectionName",

       //Schema = "someSchemaIfDesired"};

    var factory = new IdentityServerServiceFactory();

    factory.RegisterOperationalServices(efConfig);

    v 数据清理

    大部分的操作数据已经过期。 很可能需要移除这个陈旧的数据。 可以承载IdentityServer以外的应用程序或数据库本身(通过各种机制)。 如果需要你执行这个清理应用程序代码,那么TokenCleanup类提供协助。 它接受一个EntityFrameworkServiceOptions和一个Int32时间间隔(以秒为单位)来配置陈旧的数据清理的频率。 它将异步连接到数据库,可以配置为:

    var efConfig = new EntityFrameworkServiceOptions {

        ConnectionString = connString,

        //Schema = "foo"};

    var cleanup = new TokenCleanup(efConfig, 10);cleanup.Start();

    第十二章模式变化和迁移

    IdentityServer3增强,数据库模式很可能会发生变化。 的预期模式变化,建议(和预期)托管应用程序负责处理这些模式会随着时间而改变。

    实体框架提供了迁移作为一个方法来处理模式变化和更新您的数据库与变化。

    v dbcontext

    有三种不同的DbContext派生类中包含的EF的实现。 它们是:

    l ClientConfigurationDbContext

    l ScopeConfigurationDbContext

    l OperationalDbContext

    这些需要,因为数据库上下文类(即在一个不同的装配。Thinktecture.IdentityServer3.EntityFramework比托管应用程序)

    v 使迁移

    迁移必须为每个数据库创建上下文类。 使迁移的所有数据库上下文类,可以使用下面的命令从包管理器控制台:

    Enable-Migrations -MigrationsDirectory MigrationsClientConfiguration -ContextTypeName ClientConfigurationDbContext -ContextAssemblyName Thinktecture.IdentityServer3.EntityFramework -ConnectionStringName IdSvr3Config

     

    Enable-Migrations -MigrationsDirectory MigrationsScopeConfiguration -ContextTypeName ScopeConfigurationDbContext -ContextAssemblyName Thinktecture.IdentityServer3.EntityFramework -ConnectionStringName IdSvr3Config

     

    Enable-Migrations -MigrationsDirectory MigrationsOperationalConfiguration -ContextTypeName OperationalDbContext -ContextAssemblyName Thinktecture.IdentityServer3.EntityFramework -ConnectionStringName IdSvr3Config

    最初的模式必须被定义为每个迁移(又一个),因此:

    Add-Migration -Name InitialCreate -ConfigurationTypeName Host.Migrations.ScopeConfiguration.Configuration -ConnectionStringName IdSvr3Config

     

    Add-Migration -Name InitialCreate -ConfigurationTypeName Host.Migrations.ClientConfiguration.Configuration -ConnectionStringName IdSvr3Config

     

    Add-Migration -Name InitialCreate -ConfigurationTypeName Host.Migrations.OperationalConfiguration.Configuration -ConnectionStringName IdSvr3Config

    然后可以创建数据库:

    Update-Database -ConfigurationTypeName Host.Migrations.ClientConfiguration.Configuration -ConnectionStringName IdSvr3Config

    Update-Database -ConfigurationTypeName Host.Migrations.ScopeConfiguration.Configuration -ConnectionStringName IdSvr3Config

    Update-Database -ConfigurationTypeName Host.Migrations.OperationalConfiguration.Configuration -ConnectionStringName IdSvr3Config

    一旦你的应用程序更新到新版本,那么您可以使用Add-MigrationUpdate-Database更新到新模式。 检查英孚文档为更多的细节。

    第十三章ws - federation

    v 添加ws - federation支持

    IdentityServer支持ws - federation充当一个身份提供商 允许外部身份验证通过ws - federation协议。 外部认证见在这里

    这部分是关于添加ws - federation IdentityServer3身份提供者的功能。

    ws - federation IdentityServer3支持是一个插件,使用的实现。 NET 4.5 System.IdentityModel。 服务组装。 您首先需要安装插件使用Nuget:

     install-package Thinktecture.IdentityServer3.WsFederation

    然后您可以连接插件实现PluginConfiguration回调的IdentityServerOptions类是这样的:

      public void Configuration(IAppBuilder appBuilder)

     {

         var options = new IdentityServerOptions

         {

             SiteName = "Thinktecture IdentityServer3 with WsFed",

     

             SigningCertificate = Certificate.Get(),

             Factory = factory,

             PluginConfiguration = ConfigureWsFederation

         };

     

         appBuilder.UseIdentityServer(options);

     }

     

     private void ConfigureWsFederation(IAppBuilder pluginApp, IdentityServerOptions options)

     {

         var factory = new WsFederationServiceFactory(options.Factory);

     

         // data sources for in-memory services

         factory.Register(new Registration<IEnumerable<RelyingParty>>(RelyingParties.Get()));

         factory.RelyingPartyService = new Registration<IRelyingPartyService>(typeof(InMemoryRelyingPartyService));

     

         var wsFedOptions = new WsFederationPluginOptions

         {

             IdentityServerOptions = options,

             Factory = factory

         };

     

         pluginApp.UseWsFederationPlugin(wsFedOptions);

     }

     

    相当于一个OpenID连接或在ws - federation OAuth2端叫做依赖方。 类似于其他内存工厂(在这里)ws - federation插件对检索的内置支持从一个内存中的服务依赖方。

    看到在这里

    v 定义依赖方

    的RelyingParty类模型依赖方:

    • Name
      • 显示名称
    • Enabled
      • (默认为启用/禁用true)
    • Realm
      • 依赖方的唯一标识符
    • ReplyUrl
      • URL发送令牌回
    • TokenType
      • 类型的令牌(默认为SAML2),支持以下类型:
        • urn:oasis:names:tc:SAML:1.0:assertion (SAML 1.1)
        • urn:oasis:names:tc:SAML:2.0:assertion (SAML 2.0)
        • urn:ietf:params:oauth:token-type:jwt (JWT)
    • TokenLifeTime
      • 令牌一生在几分钟内(默认值为480)
    • EncryptingCertificate
      • 证书加密令牌(SAML)
    • IncludeAllClaimsForUser
      • 包括所有可用的用户令牌(而不是显式的映射)
    • DefaultClaimTypeMappingPrefix
      • 默认的输出要求类型如果前缀IncludeAllClaimsForUser但不存在显式的映射。
    • ClaimMappings
      • 允许设置一个映射表从内部Claims类型外向Claims类型(情况 有地图的冲动如name来http://schemas.xmlsoap.org/ws/2005/05/identity/claims/name)
    • SamlNameIdentifierFormat
      • 允许设置SAML的SAML名称标识符格式名称ID
    • SignatureAlgorithm
      • 允许设置令牌的签名算法(默认为RSASHA256)
    • DigestAlgorithm
      • 允许设置摘要算法(默认为SHA256)

     

    v 端点

    ws - federation插件包含两个端点

    v 登录/出

    这是主要的ws - federation端点的签字。

    支持参数:

    • wa
      • 必须是wsignin1.0签署的或wsignout1.0对于签署
    • wtrealm
      • 依赖方的领域
    • wctx
      • 上下文是圆绊倒依赖方(类似state在OAuth2)
    • whr
      • 外部身份提供商使用的名称(跳过选择屏幕)

    示例(可读性编码删除):

    /wsfed?wa=wsignin1.0&wtrealm=rp1&whr=Google

    v 元数据

    返回元数据文档:

    /wsfed/metadata

    第十四章资源

    v 资源

    Ø 规范

    OpenID连接核心规范

    OAuth2规范

    OAuth2威胁模型

    Ø 文章

    混合流

    注销后重定向

    注销后自动重定向

    在Mono IdentityServer

    测试Web api和邮递员

    添加一个SAML2p外部身份验证提供者

    斯科特·布雷迪:IdentityServer3独立实现pt。1

    斯科特·布雷迪:IdentityServer3独立实现pt。2

    斯科特·布雷迪:IdentityServer3独立实现pt。3

    威廉姆斯Reece:FIPS遵从性

    Ø 视频

    概述视频(预览版)

    样品介绍视频(预览版)

    引入IdentityManager

    介绍OpenID连接,OAuth2 IdentityServer

    Web API授权和访问控制——做对的! ?

    身份管理与ASP。 净第1部分

    身份管理与ASP。 净第2部分

    Ø 培训

    布鲁克和多明尼克的身份和访问控制的现代Web应用程序和api车间

    建立和确保一个RESTful api的多个客户端ASP。 净在PluralSight

    v 社区插件

    MongoDb支持

    ElasticSearch / Kibana EventService

    为多种语言本地化服务(包括海盗!)

    复述,缓存

    Powershell Cmdlets实体框架的持久性

    第十五章中间件为外部认证

    v 微软的武士刀

    ws - federation

    OpenID连接

    谷歌

    推特

    微软账户

    脸谱网

    v SAML2p

    KentorIT认证服务——看到也博客

    v 其他社区的贡献

    OWIN OAuth提供者

     

     

     

     

  • 相关阅读:
    s3fs 挂载minio为本地文件系统
    P5787 线段树分治
    P5494 线段树分裂
    P1552 [APIO2012]派遣
    CF600E Lomsat gelral(线段树合并)
    P5283 异或粽子
    P4735 最大异或和(可持久化 trie)
    P3960 列队
    bzoj4316 小C的独立集
    P5021 赛道修建
  • 原文地址:https://www.cnblogs.com/weibinlong/p/5148849.html
Copyright © 2020-2023  润新知