IdentityServer4-快速入门

1、设置和概述html

2、使用客户端凭证保护APInode

3、使用密码保护APIgit

4、使用OpenID Connect添加用户验证github

5、添加对外部认证的支持算法

6、切换到Hybrid Flow并添加API访问权限sql

7、Using ASP.NET Core Identity数据库

8、添加一个JavaScript客户端json

9、使用EntityFramework core进行配置和操做数据后端

10、社区快速入门&样品api

 

1、设置和概述

1,使用 dotnet new mvc 建立一个mvc项目

2,nuget IdentityServer4 

3,修改Startup

public class Startup
{
    public void ConfigureServices(IServiceCollection services)
    {
        services.AddIdentityServer()//在DI中注册IdentityServer服务,它还为运行时状态注册一个内存存储
            .AddDeveloperSigningCredential();//设置临时签名凭证
    }

    public void Configure(IApplicationBuilder app, IHostingEnvironment env)
    {
        if (env.IsDevelopment())
        {
            app.UseDeveloperExceptionPage();
        }

        app.UseIdentityServer();
    }
}

 

2、使用客户端凭证保护API

在这个场景中,咱们将定义一个API和一个但愿访问它的客户端。客户端将在IdentityServer中请求访问令牌,并使用它得到对API的访问。 

定义API

建立一个Config,cs文件,在该文件中定义您但愿保护的系统中的资源,例如api。

public static IEnumerable<ApiResource> GetApiResources()
{
    return new List<ApiResource>
    {
        new ApiResource("api1", "My API")
    };
}

定义Client

定义访问这个api的client

对于此场景,客户端将没有交互式用户,并将使用所谓的client secret与IdentityServer进行身份验证。将如下代码添加到配置中。cs文件:

        public static IEnumerable<Client> GetClient()
        {
            return new List<Client>()
            {
                new Client(){
                    ClientId="client",
                    //没有交互式用户,使用clientid/secret进行身份验证
                    AllowedGrantTypes=GrantTypes.ClientCredentials,
                    //secret认证
                    ClientSecrets={new Secret("secret".Sha256())},
                    //客户端有权访问的范围
                    AllowedScopes={"api"}
                }
            };
        }

配置IdentityServer

在ConfigureServices添加AddInMemoryApiResources与AddInMemoryClients方法

public void ConfigureServices(IServiceCollection services)
{
    // configure identity server with in-memory stores, keys, clients and resources
    services.AddIdentityServer()
        .AddDeveloperSigningCredential()
        .AddInMemoryApiResources(Config.GetApiResources())
        .AddInMemoryClients(Config.GetClients());
}

打开 http://localhost:5000/.well-known/openid-configuration 你应该看看所谓的发现文件。您的客户端和api将使用它来下载必要的配置数据。

添加一个API项目

添加一个api项目,将url设置为 http://localhost:5001 

1,添加controller

[Route("identity")]
[Authorize]
public class IdentityController : ControllerBase
{
    [HttpGet]
    public IActionResult Get()
    {
        return new JsonResult(from c in User.Claims select new { c.Type, c.Value });
    }
}

2,配置

最后一步是将身份验证服务添加到DI,将身份验证中间件添加到管道。这些将会:

  • 验证传入的令牌以确保它来自受信任的颁发者
  • 验证该令牌是否适用于此api(又名scope)

nuget IdentityServer4.AccessTokenValidation 

更新Startup以下

public class Startup
{
    public void ConfigureServices(IServiceCollection services)
    {
        services.AddMvcCore()
            .AddAuthorization()
            .AddJsonFormatters();
        //AddAuthentication将认证服务添加到DI,并将“Bearer”配置为默认方案。
        services.AddAuthentication("Bearer")
            //将IdentityServer访问令牌验证处理程序添加到DI中以供验证服务使用
            .AddIdentityServerAuthentication(options =>
            {
                options.Authority = "http://localhost:5000";
                options.RequireHttpsMetadata = false;

                options.ApiName = "api1";
            });
    }

    public void Configure(IApplicationBuilder app)
    {
        //UseAuthentication将认证中间件添加到管道,所以每次调用主机时都会自动执行认证。
        app.UseAuthentication();

        app.UseMvc();
    }
}

若是使用浏览器导航到控制器(http://localhost:5001/identity),则应该获得401状态码做为回报。这意味着您的API须要凭据。
就是这样,API如今由IdentityServer保护。

建立client

添加一个console项目

IdentityServer中的令牌端点实现OAuth 2.0协议,您可使用原始HTTP访问它。可是,咱们有一个名为IdentityModel的客户端库,它将协议交互封装在一个易于使用的API中。

nuget IdentityModel 

IdentityModel包含一个用于发现端点的客户端库。 这样您只须要知道IdentityServer的基地址 - 能够从元数据中读取实际的端点地址:

// 从元数据发现端点
var disco = await DiscoveryClient.GetAsync("http://localhost:5000");
if (disco.IsError)
{
    Console.WriteLine(disco.Error);
    return;
}

接下来,您可使用TokenClient类来请求令牌。 要建立实例,您须要传入令牌端点地址,client ID和secret。

接下来,您可使用requestcredenticlientalsasync方法为您的API请求一个令牌:

// request token
var tokenClient = new TokenClient(disco.TokenEndpoint, "client", "secret");
var tokenResponse = await tokenClient.RequestClientCredentialsAsync("api1");

if (tokenResponse.IsError)
{
    Console.WriteLine(tokenResponse.Error);
    return;
}

Console.WriteLine(tokenResponse.Json);//将access token赋值到https://jwt.io/能够查看详细信息

最后一步是调用API。
要将访问令牌发送到API,一般使用HTTP受权头。这是使用SetBearerToken扩展方法完成的:

// call api
var client = new HttpClient();
client.SetBearerToken(tokenResponse.AccessToken);

var response = await client.GetAsync("http://localhost:5001/identity");
if (!response.IsSuccessStatusCode)
{
    Console.WriteLine(response.StatusCode);
}
else
{
    var content = await response.Content.ReadAsStringAsync();
    Console.WriteLine(JArray.Parse(content));
}

 案例下载:https://pan.baidu.com/s/1xYxymmnn6giMUphnnCGaaQ

 

3、使用密码保护API

客户端输入用户名和密码获取访问令牌,经过访问令牌访问资源,该规范建议仅将“资源全部者密码授予”用于“可信”应用程序

添加用户

TestUser类表示测试用户。 让咱们经过将如下代码添加到咱们的配置类来建立几个用户:

首先将如下using语句添加到Config.cs文件中:

using IdentityServer4.Test;

public static List<TestUser> GetUsers()
{
    return new List<TestUser>
    {
        new TestUser
        {
            SubjectId = "1",
            Username = "alice",
            Password = "password"
        },
        new TestUser
        {
            SubjectId = "2",
            Username = "bob",
            Password = "password"
        }
    };
}

而后向IdentityServer注册测试用户:

public void ConfigureServices(IServiceCollection services)
{
    // configure identity server with in-memory stores, keys, clients and scopes
    services.AddIdentityServer()
        .AddDeveloperSigningCredential()
        .AddInMemoryApiResources(Config.GetApiResources())
        .AddInMemoryClients(Config.GetClients())
        .AddTestUsers(Config.GetUsers());
}

AddTestUsers扩展方法在底层作了几件事情

  • 增长了对资源全部者密码受权的支持
  • 增长了对登陆UI一般使用的用户相关服务的支持
  • 基于测试用户添加了对配置文件服务的支持

配置一个支持密码受权的客户端

public static IEnumerable<Client> GetClients()
{
    return new List<Client>
    {
        // other clients omitted...

        // resource owner password grant client
        new Client
        {
            ClientId = "ro.client",
            AllowedGrantTypes = GrantTypes.ResourceOwnerPassword,

            ClientSecrets =
            {
                new Secret("secret".Sha256())
            },
            AllowedScopes = { "api1" }
        }
    };
}

使用密码授予请求令牌

客户端看起来很是相似于咱们为客户端凭据受权所作的操做。 主要区别在于客户端会以某种方式收集用户的密码,并在令牌请求期间将其发送给令牌服务。

// request token
var tokenClient = new TokenClient(disco.TokenEndpoint, "ro.client", "secret");
var tokenResponse = await tokenClient.RequestResourceOwnerPasswordAsync("alice", "password", "api1");

if (tokenResponse.IsError)
{
    Console.WriteLine(tokenResponse.Error);
    return;
}

Console.WriteLine(tokenResponse.Json);
Console.WriteLine("\n\n");

案例下载:https://pan.baidu.com/s/1q-UYw_34hX00MqxiZp524g

 

4、使用OpenID Connect添加用户验证

添加UI

OpenID Connect所需的全部协议支持都已内置到IdentityServer中。 您须要为登陆,注销,赞成和错误提供必要的UI部分。

虽然每一个IdentityServer实现的外观和感受以及确切的工做流程可能老是不一样,但咱们提供了一个基于MVC的示例UI,您能够将其用做起点。

这个用户界面能够在Quickstart UI仓库中找到。 您能够克隆或下载此repo,并将控制器,视图,模型和CSS放入您的IdentityServer Web应用程序。

建立一个MVC应用,而后将 Quickstart UI复制到项目中,nuget  IdentityServer4 。并配置Startup:

public void ConfigureServices(IServiceCollection services)
{
    services.AddMvc();

    // configure identity server with in-memory stores, keys, clients and scopes
    services.AddIdentityServer()
        .AddDeveloperSigningCredential()
        .AddInMemoryApiResources(Config.GetApiResources())
        .AddInMemoryClients(Config.GetClients())
        .AddTestUsers(Config.GetUsers());
}
public void Configure(IApplicationBuilder app, IHostingEnvironment env)
{
    if (env.IsDevelopment())
    {
        app.UseDeveloperExceptionPage();
    }

    app.UseIdentityServer();

    app.UseStaticFiles();
    app.UseMvcWithDefaultRoute();
}

建立一个MVC客户端 

要将OpenID Connect身份验证支持添加到MVC应用程序,请将如下内容添加到启动时的ConfigureServices中: 

            //AddAuthentication将认证服务添加到DI
            services.AddAuthentication(options=>{
                options.DefaultScheme="Cookies";//设置Cookies为主要认证手段
                options.DefaultChallengeScheme="oidc";//当须要登陆时使用OpenID Connect方案
            })
            .AddCookie("Cookies")//使用AddCookie添加能够处理cookie的处理程序。
            .AddOpenIdConnect("oidc",options=>{
                options.SignInScheme="Cookies";
                options.Authority="http://localhost:5000";
                options.RequireHttpsMetadata=false;
                options.ClientId="client";
                options.SaveTokens=true;
            });

AddOpenIdConnect用于配置执行OpenID链接协议的处理程序。受权代表咱们信任身份服务器。而后咱们经过ClientId识别这个客户端。一旦OpenID链接协议完成,SignInScheme将使用cookie处理程序发出cookie。

savetoken用于将标识符从IdentityServer中持久化到cookie中(稍后须要它们)。

此外,咱们关闭了JWT声明类型映射,以容许众所周知的声明(例如'sub'和'idp')经过非混淆的声明: 

 JwtSecurityTokenHandler.DefaultInboundClaimTypeMap.Clear();  

 而后为了确保验证服务在每一个请求上执行,请添加UseAuthentication以在启动时进行配置:

public void Configure(IApplicationBuilder app, IHostingEnvironment env)
{
    if (env.IsDevelopment())
    {
        app.UseDeveloperExceptionPage();
    }
    else
    {
        app.UseExceptionHandler("/Home/Error");
    }

    app.UseAuthentication();//认证中间件应该在管道中的MVC以前添加。

    app.UseStaticFiles();
    app.UseMvcWithDefaultRoute();
}

添加对OpenID Connect Identity Scopes的支持

与OAuth 2.0相似,OpenID Connect也使用了做用域概念。一样,做用域表示您但愿保护和客户端但愿访问的内容。与OAuth相反,OIDC中的做用域并不表示api,而是用户id、名称或电子邮件地址等标识数据。

在Config.cs中建立一个IdentityResource对象集合,添加对标准openid(subject id)和profile(名字,姓氏等)做用域的支持

public static IEnumerable<IdentityResource> GetIdentityResources()
{
    return new List<IdentityResource>
    {
        new IdentityResources.OpenId(),
        new IdentityResources.Profile(),
    };
}

修改Startup

public void ConfigureServices(IServiceCollection services)
{
    services.AddMvc();

    // configure identity server with in-memory stores, keys, clients and scopes
    services.AddIdentityServer()
        .AddDeveloperSigningCredential()
        .AddInMemoryIdentityResources(Config.GetIdentityResources())
        .AddInMemoryApiResources(Config.GetApiResources())
        .AddInMemoryClients(Config.GetClients())
        .AddTestUsers(Config.GetUsers());
}

为OpenID Connect隐式流添加客户端

最后一步是将MVC客户端的新配置条目添加到IdentityServer。

基于OpenID Connect的客户端与咱们迄今添加的OAuth 2.0客户端很是类似。 但因为OIDC中的流程始终是交互式的,所以咱们须要将一些重定向URL添加到咱们的配置中。

将如下内容添加到客户端配置中:

public static IEnumerable<Client> GetClients()
{
    return new List<Client>
    {
        // 其余客户被忽略...

        // OpenID Connect隐式流程客户端(MVC)
        new Client
        {
            ClientId = "mvc",
            ClientName = "MVC Client",
            AllowedGrantTypes = GrantTypes.Implicit,

            // 登陆后重定向到哪里
            RedirectUris = { "http://localhost:5001/signin-oidc" },

            // 注销后重定向到哪里
            PostLogoutRedirectUris = { "http://localhost:5001/signout-callback-oidc" },

            AllowedScopes = new List<string>
            {
                IdentityServerConstants.StandardScopes.OpenId,
                IdentityServerConstants.StandardScopes.Profile
            }
        }
    };
}

 测试客户端

如今,最终全部的东西都应该适用于新的MVC客户端。

经过导航到受保护的控制器操做来触发身份验证握手。 您应该看到重定向到IdentityServer的登陆页面。

 案例下载:https://pan.baidu.com/s/1XHL4eRzp3oqIK3ubMF-NLQ

 

5、添加对外部认证的支持

接下来咱们将添加对外部认证的支持。 这很是简单,由于您真正须要的只是一个兼容ASP.NET Core的身份验证处理程序。 

ASP.NET Core自己也支持Google,Facebook,Twitter,Microsoft账户和OpenID Connect。 另外你能够在这里找到许多其余认证提供者的实现。

添加Google支持

要可以使用谷歌进行身份验证,首先须要向它们注册。这是在开发人员控制台完成的。建立一个新项目,启用谷歌+ API,并经过将/signin-google路径添加到基本地址(例如http://localhost:5000/signin- Google)来配置本地标识服务器的回调地址。

若是您正在端口5000上运行——您可使用下面代码片断中的客户端id/secret,由于这是咱们预先注册的。

首先添加Google身份验证处理程序到DI。 这是经过将此片断添加到启动时的ConfigureServices中完成的:

public void ConfigureServices(IServiceCollection services)
{
    services.AddMvc();

    // configure identity server with in-memory stores, keys, clients and scopes
    services.AddIdentityServer()
        .AddDeveloperSigningCredential()
        .AddInMemoryIdentityResources(Config.GetIdentityResources())
        .AddInMemoryApiResources(Config.GetApiResources())
        .AddInMemoryClients(Config.GetClients())
        .AddTestUsers(Config.GetUsers());

    services.AddAuthentication()
        .AddGoogle("Google", options =>
        {
            options.SignInScheme = IdentityServerConstants.ExternalCookieAuthenticationScheme;

            options.ClientId = "434483408261-55tc8n0cs4ff1fe21ea8df2o443v2iuc.apps.googleusercontent.com";
            options.ClientSecret = "3gcoTrEDPPJ0ukn_aYYT6PWo";
        });
}

默认状况下,IdentityServer专门为外部认证的结果配置cookie处理程序(使用基于常量IdentityServerConstants.ExternalCookieAuthenticationScheme的方案)。 Google处理程序的配置而后使用该cookie处理程序。 为了更好地理解这是如何完成的,请参阅Quickstart文件夹下的AccountController类。

进一步实验

您能够添加一个额外的外部提供者。 咱们有一个云托管的IdentityServer4演示版本,您可使用OpenID Connect进行集成。

将OpenId Connect处理程序添加到DI中:

services.AddAuthentication()
    .AddGoogle("Google", options =>
    {
        options.SignInScheme = IdentityServerConstants.ExternalCookieAuthenticationScheme;

        options.ClientId = "434483408261-55tc8n0cs4ff1fe21ea8df2o443v2iuc.apps.googleusercontent.com";
        options.ClientSecret = "3gcoTrEDPPJ0ukn_aYYT6PWo";
    })
    .AddOpenIdConnect("oidc", "OpenID Connect", options =>
    {
        options.SignInScheme = IdentityServerConstants.ExternalCookieAuthenticationScheme;
        options.SignOutScheme = IdentityServerConstants.SignoutScheme;

        options.Authority = "https://demo.identityserver.io/";
        options.ClientId = "implicit";

        options.TokenValidationParameters = new TokenValidationParameters
        {
            NameClaimType = "name",
            RoleClaimType = "role"
        };
    });

 

6、切换到Hybrid Flow并添加API访问权限

在前面的快速启动中,咱们研究了API访问和用户身份验证。如今咱们要把这两个部分结合起来。 

OpenID Connect & OAuth 2.0组合的美妙之处在于,您能够经过一个协议和一个与令牌服务的交换来实现。

在前面的quickstart中,咱们使用了OpenID链接隐式流。在隐式流中,全部令牌都经过浏览器传输,这对于身份令牌来讲是彻底没问题的。如今咱们还想请求一个访问令牌。

访问令牌比身份令牌更加敏感,若是不须要,咱们不但愿将它们暴露于“外部”世界。 OpenID Connect包含一个名为“混合流”的流程,它可让咱们一箭双鵰,身份令牌经过浏览器通道传输,所以客户能够在作更多工做以前验证它。 若是验证成功,客户端会打开令牌服务的后端通道来检索访问令牌。

修改client配置

没有多少必要的修改。首先,咱们但愿容许客户端使用混合流,此外,咱们还但愿客户端容许执行服务器到服务器的API调用,这些调用不在用户的上下文中(这与咱们的客户端凭证quickstart很是类似)。使用AllowedGrantTypes属性表示。

接下来,咱们须要添加一个客户端secret。这将用于检索后通道上的访问令牌。

最后,咱们还为客户端提供了对offline_access范围的访问权限——这容许为长期存在的API访问请求刷新令牌:

new Client
{
    ClientId = "mvc",
    ClientName = "MVC Client",
    AllowedGrantTypes = GrantTypes.HybridAndClientCredentials,

    ClientSecrets =
    {
        new Secret("secret".Sha256())
    },

    RedirectUris           = { "http://localhost:5002/signin-oidc" },
    PostLogoutRedirectUris = { "http://localhost:5002/signout-callback-oidc" },

    AllowedScopes =
    {
        IdentityServerConstants.StandardScopes.OpenId,
        IdentityServerConstants.StandardScopes.Profile,
        "api1"
    },
    AllowOfflineAccess = true
};

修改MVC客户端

MVC客户端的修改也是最小的——ASP.NET核心的OpenID链接处理器对混合流有内置的支持,因此咱们只须要更改一些配置值。

咱们配置 ClientSecret 以匹配IdentityServer上的secret。添加 offline_access 和 api1 做用域,并将 ResponseType 设置为 code id_token (这基本上意味着“使用混合流”)

.AddOpenIdConnect("oidc", options =>
{
    options.SignInScheme = "Cookies";

    options.Authority = "http://localhost:5000";
    options.RequireHttpsMetadata = false;

    options.ClientId = "mvc";
    options.ClientSecret = "secret";
    options.ResponseType = "code id_token";

    options.SaveTokens = true;
    options.GetClaimsFromUserInfoEndpoint = true;

    options.Scope.Add("api1");
    options.Scope.Add("offline_access");
});

当您运行MVC客户端时,除了赞成屏幕如今要求您提供额外的API和 offline access scope外,没有太大区别。

使用访问令牌

OpenID链接中间件为您自动保存令牌(标识、访问和刷新)。这就是savetoken设置的做用。

技术上,令牌存储在cookie的属性部分。 访问它们的最简单方法是使用Microsoft.AspNetCore.Authentication命名空间中的扩展方法。

例如在您的声明视图上:

<dt>access token</dt>
<dd>@await ViewContext.HttpContext.GetTokenAsync("access_token")</dd>

<dt>refresh token</dt>
<dd>@await ViewContext.HttpContext.GetTokenAsync("refresh_token")</dd>

要使用访问令牌访问API,您只需检索令牌并将其设置在您的HttpClient上:

public async Task<IActionResult> CallApiUsingUserAccessToken()
{
    var accessToken = await HttpContext.GetTokenAsync("access_token");

    var client = new HttpClient();
    client.SetBearerToken(accessToken);
    var content = await client.GetStringAsync("http://localhost:5001/identity");

    ViewBag.Json = JArray.Parse(content).ToString();
    return View("json");
}

 

7、Using ASP.NET Core Identity

IdentityServer旨在提供灵活性,其中的一部分容许您为用户及其数据使用任何数据库(包括密码)。 若是你从一个新的用户数据库开始,那么ASP.NET Identity是你能够选择的一个选项。 本快速入门介绍了如何将Identity Identity与Identity Identity一块儿使用。 

这个快速入门假设你已经完成了全部的快速入门。 快速入门使用ASP.NET Identity的方法是从Visual Studio中的ASP.NET Identity模板建立一个新项目。 这个新项目将取代以前在快速入门中从头开始构建的IdentityServer项目。 此解决方案中的全部其余项目(针对客户端和API)将保持不变。

新建ASP.NET Identity项目 

第一步是为您的解决方案添加一个ASP.NET Identity的新项目。 鉴于ASP.NET身份须要大量代码,所以使用Visual Studio中的模板是有意义的。 您最终将删除IdentityServer的旧项目(假设您正在关注其余快速入门),但您须要迁移几个项目(或按照以前的快速入门中所述从头开始重写)。

nuget  dotnet new mvc --auth Individual 

修改hosting

不要忘记修改主机(如此处所述)以在端口5000上运行。这很是重要,所以现有客户端和API项目将继续运行。

添加IdentityServer包 

添加IdentityServer4.AspNetIdentity NuGet包。 这取决于IdentityServer4包,所以会自动添加为传递依赖项。

Scopes和Clients配置

尽管这是IdentityServer的一个新项目,但咱们仍须要与以前的快速入门相同的范围和客户端配置。 将用于以前快速入门的配置类(在Config.cs中)复制到此新项目中。

new Client
{
    ClientId = "mvc",
    ClientName = "MVC Client",
    AllowedGrantTypes = GrantTypes.HybridAndClientCredentials,

    RequireConsent = false,

    ClientSecrets =
    {
        new Secret("secret".Sha256())
    },

    RedirectUris           = { "http://localhost:5002/signin-oidc" },
    PostLogoutRedirectUris = { "http://localhost:5002/signout-callback-oidc" },

    AllowedScopes =
    {
        IdentityServerConstants.StandardScopes.OpenId,
        IdentityServerConstants.StandardScopes.Profile,
        "api1"
    },
    AllowOfflineAccess = true
}

配置IdentityServer

与前面同样,须要在Configure reservices中和Startup.cs中配置IdentityServer。

ConfigureServices

public void ConfigureServices(IServiceCollection services)
{
    services.AddDbContext<ApplicationDbContext>(options =>
        options.UseSqlServer(Configuration.GetConnectionString("DefaultConnection")));

    services.AddIdentity<ApplicationUser, IdentityRole>()
        .AddEntityFrameworkStores<ApplicationDbContext>()
        .AddDefaultTokenProviders();

    // Add application services.
    services.AddTransient<IEmailSender, EmailSender>();

    services.AddMvc();

    // configure identity server with in-memory stores, keys, clients and scopes
    services.AddIdentityServer()
        .AddDeveloperSigningCredential()
        .AddInMemoryPersistedGrants()
        .AddInMemoryIdentityResources(Config.GetIdentityResources())
        .AddInMemoryApiResources(Config.GetApiResources())
        .AddInMemoryClients(Config.GetClients())
        .AddAspNetIdentity<ApplicationUser>();
}

Configure

public void Configure(IApplicationBuilder app, IHostingEnvironment env)
{
    if (env.IsDevelopment())
    {
        app.UseDeveloperExceptionPage();
        app.UseBrowserLink();
        app.UseDatabaseErrorPage();
    }
    else
    {
        app.UseExceptionHandler("/Home/Error");
    }

    app.UseStaticFiles();

    // app.UseAuthentication(); // not needed, since UseIdentityServer adds the authentication middleware
    app.UseIdentityServer();

    app.UseMvc(routes =>
    {
        routes.MapRoute(
            name: "default",
            template: "{controller=Home}/{action=Index}/{id?}");
    });
}

建立用户数据库

鉴于这是一个新的ASP.NET Identity项目,您将须要建立数据库。 您能够经过从项目目录运行命令提示符并运行 dotnet ef database update -c ApplicationDbContext 来执行此操做

案例下载:https://pan.baidu.com/s/1ZxVHNYApxyZMGcDg-XlTkg 

8、添加一个JavaScript客户端

这个快速入门将展现如何构建JavaScript客户端应用程序。 用户将登陆到IdentityServer,使用IdentityServer发出的访问令牌调用Web API,并注销IdentityServer。

添加一个JavaScript的客户端项目

添加一个空项目 

添加静态文件中间件

鉴于此项目主要用于运行客户端,咱们须要ASP.NET Core来提供构成咱们应用程序的静态HTML和JavaScript文件。 静态文件中间件旨在执行此操做。

在Configure方法中的Startup.cs中注册静态文件中间件:

public void Configure(IApplicationBuilder app)
{
    app.UseDefaultFiles();
    app.UseStaticFiles();
}

这个中间件如今将从应用程序的〜/ wwwroot文件夹中提供静态文件。 这是咱们将放置HTML和JavaScript文件的地方。

引用oidc-client

在MVC项目中,咱们使用一个库来处理OpenID Connect协议。 在这个项目中,咱们须要一个相似的库,除了一个在JavaScript中工做而且被设计为在浏览器中运行的库。 oidc-client库就是这样一个库。 它能够经过NPM,Bower,以及从github直接下载。

NPM

若是您想使用NPM下载oidc-client,请按照如下步骤操做:

将一个新的NPM包文件添加到您的项目中,并将其命名为package.json。

在package.json中添加一个依赖到oidc-client:

"dependencies": {
  "oidc-client": "1.4.1"
}

一旦保存了该文件,Visual Studio应该自动将这些软件包恢复到名为node_modules的文件夹中。

在〜/node_modules/oidc-client/dist文件夹中找到名为oidc-client.js的文件,并将其复制到应用程序的〜/wwwroot文件夹中。 有更复杂的方法将您的NPM软件包复制到〜/wwwroot中,但这些技术不在本快速入门的范围以内。

添加您的HTML和JavaScript文件

在〜/wwwroot中,添加一个名为index.html和callback.html的HTML文件,并添加一个名为app.js的JavaScript文件。

index.html

这将是咱们应用程序的主页面。 它将包含用于登陆,注销并调用Web API的按钮的HTML。 它还将包含<script>标签以包含咱们的两个JavaScript文件。 它还将包含用于向用户显示消息的<pre>。

<!DOCTYPE html>
<html>
<head>
    <meta charset="utf-8" />
    <title></title>
</head>
<body>
    <button id="login">Login</button>
    <button id="api">Call API</button>
    <button id="logout">Logout</button>

    <pre id="results"></pre>

    <script src="oidc-client.js"></script>
    <script src="app.js"></script>
</body>
</html>
index.html

app.js

这将包含咱们的应用程序的主要代码。 首先要添加一个辅助函数来将消息记录到<pre>: 

function log() {
    document.getElementById('results').innerText = '';

    Array.prototype.forEach.call(arguments, function (msg) {
        if (msg instanceof Error) {
            msg = "Error: " + msg.message;
        }
        else if (typeof msg !== 'string') {
            msg = JSON.stringify(msg, null, 2);
        }
        document.getElementById('results').innerHTML += msg + '\r\n';
    });
}
app.js

接下来,添加代码将“click”事件处理程序注册到三个按钮:

document.getElementById("login").addEventListener("click", login, false);
document.getElementById("api").addEventListener("click", api, false);
document.getElementById("logout").addEventListener("click", logout, false);

接下来,咱们可使用oidc-client库中的UserManager类来管理OpenID Connect协议。 它须要相似的配置,这在MVC客户端中是必需的(虽然值不一样)。 添加此代码以配置和实例化UserManager:

var config = {
    authority: "http://localhost:5000",
    client_id: "js",
    redirect_uri: "http://localhost:5003/callback.html",
    response_type: "id_token token",
    scope:"openid profile api1",
    post_logout_redirect_uri : "http://localhost:5003/index.html",
};
var mgr = new Oidc.UserManager(config);

接下来,UserManager提供一个getUser API来知道用户是否登陆到JavaScript应用程序。 它使用JavaScript Promise异步返回结果。 返回的用户对象具备包含用户声明的配置文件属性。 添加此代码以检测用户是否已登陆JavaScript应用程序:

mgr.getUser().then(function (user) {
    if (user) {
        log("User logged in", user.profile);
    }
    else {
        log("User not logged in");
    }
});

接下来,咱们要实现登陆,api和注销功能。 UserManager提供signinRedirect来登陆用户,并提供signoutRedirect登陆用户。 咱们在上面的代码中得到的用户对象也具备access_token属性,可用于经过Web API进行身份验证。 access_token将经过Bearer方案的Authorization头传递给Web API。 添加此代码以在咱们的应用程序中实现这三个功能:

function login() {
    mgr.signinRedirect();
}

function api() {
    mgr.getUser().then(function (user) {
        var url = "http://localhost:5001/identity";

        var xhr = new XMLHttpRequest();
        xhr.open("GET", url);
        xhr.onload = function () {
            log(xhr.status, JSON.parse(xhr.responseText));
        }
        xhr.setRequestHeader("Authorization", "Bearer " + user.access_token);
        xhr.send();
    });
}

function logout() {
    mgr.signoutRedirect();
}

callback.html

一旦用户登陆到IdentityServer,该HTML文件就是指定的redirect_uri页面。 它将完成与IdentityServer的OpenID Connect协议登陆握手。 这个代码所有由咱们以前使用的UserManager类提供。 登陆完成后,咱们能够将用户重定向回主index.html页面。 添加此代码以完成登陆过程:

<!DOCTYPE html>
<html>
<head>
    <meta charset="utf-8" />
    <title></title>
</head>
<body>
    <script src="oidc-client.js"></script>
    <script>
        new Oidc.UserManager().signinRedirectCallback().then(function () {
            window.location = "index.html";
        }).catch(function (e) {
            console.error(e);
        });
    </script>
</body>
</html>

为JavaScript客户端添加客户端注册到IdentityServer

如今客户端应用程序已准备就绪,咱们须要在IdentityServer中为这个新的JavaScript客户端定义配置条目。 在IdentityServer项目中找到客户端配置(在Config.cs中)。 为咱们的新JavaScript应用程序添加一个新客户端到列表中。 它应该具备下面列出的配置:

// JavaScript Client
new Client
{
    ClientId = "js",
    ClientName = "JavaScript Client",
    AllowedGrantTypes = GrantTypes.Implicit,
    AllowAccessTokensViaBrowser = true,

    RedirectUris =           { "http://localhost:5003/callback.html" },
    PostLogoutRedirectUris = { "http://localhost:5003/index.html" },
    AllowedCorsOrigins =     { "http://localhost:5003" },

    AllowedScopes =
    {
        IdentityServerConstants.StandardScopes.OpenId,
        IdentityServerConstants.StandardScopes.Profile,
        "api1"
    }
}

容许使用CORS对Web API进行Ajax调用

须要配置的最后一点是在Web API项目中配置CORS。 这将容许Ajax调用从http://localhost:5003到http://localhost:5001。

Configure CORS

将CORS服务添加到Startup.cs中ConfigureServices中的依赖注入系统:

public void ConfigureServices(IServiceCollection services)
{
    services.AddMvcCore()
        .AddAuthorization()
        .AddJsonFormatters();

    services.AddAuthentication("Bearer")
        .AddIdentityServerAuthentication(options =>
        {
            options.Authority = "http://localhost:5000";
            options.RequireHttpsMetadata = false;

            options.ApiName = "api1";
        });

    services.AddCors(options =>
    {
        // this defines a CORS policy called "default"
        options.AddPolicy("default", policy =>
        {
            policy.WithOrigins("http://localhost:5003")
                .AllowAnyHeader()
                .AllowAnyMethod();
        });
    });
}

在配置中将CORS中间件添加到管道中:

public void Configure(IApplicationBuilder app)
{
    app.UseCors("default");

    app.UseAuthentication();

    app.UseMvc();
}

 案例下载:https://pan.baidu.com/s/1wFDdwjmjWIhvMIPIoZQwBw

9、使用EntityFramework核心进行配置和操做数据

IdentityServer专为扩展性而设计,其中一个可扩展点是用于IdentityServer所需数据的存储机制。 本快速入门说明如何配置IdentityServer以使用EntityFramework(EF)做为此数据的存储机制(而不是使用咱们迄今为止使用的内存中实现)。

IdentityServer4.EntityFramework

咱们正在向数据库移动两种类型的数据。 首先是配置数据(resources和client)。 第二个是IdentityServer在使用时生成的操做数据(tokens,codes和consents)。 这些商店使用接口建模,咱们在 IdentityServer4.EntityFramework  Nuget包中提供这些接口的EF实现。

经过添加对 IdentityServer4.EntityFramework  Nuget包的IdentityServer项目的引用开始。

使用SqlServer

鉴于EF的灵活性,您可使用任何EF支持的数据库。 对于这个快速入门,咱们将使用Visual Studio附带的SqlServer的LocalDb版本。

数据库模式更改和使用EF迁移

IdentityServer4.EntityFramework软件包包含从IdentityServer模型映射的实体类。 随着IdentityServer的模型更改,IdentityServer4.EntityFramework中的实体类也会更改。 在使用IdentityServer4.EntityFramework并随着时间的推移升级时,随着实体类的更改,您将负责本身的数据库模式以及对该模式所必需的更改。 管理这些变化的一种方法是使用EF迁移,这个快速入门将显示如何完成。 若是迁移不是您的偏好,那么您能够以任何您认为合适的方式管理架构更改。

用于迁移的EF工具

除了使用EF迁移跟踪模式更改外,咱们还将使用它来建立数据库中的初始模式。 这须要使用EF Core工具(更多细节在这里)。 咱们如今将添加这些内容,不幸的是,这必须经过手工编辑您的.csproj文件来完成。 经过右键单击项目并选择“编辑projectname.csproj”来编辑.csproj: 

<ItemGroup>
  <DotNetCliToolReference Include="Microsoft.EntityFrameworkCore.Tools.DotNet" Version="2.0.0" />
</ItemGroup>

配置 stores

下一步是将当前调用替换为Startup.cs中ConfigureServices方法中的AddInMemoryClients,AddInMemoryIdentityResources和AddInMemoryApiResources。 咱们将用下面的代码替换它们:

const string connectionString = @"Data Source=(LocalDb)\MSSQLLocalDB;database=IdentityServer4.Quickstart.EntityFramework-2.0.0;trusted_connection=yes;";
var migrationsAssembly = typeof(Startup).GetTypeInfo().Assembly.GetName().Name;

// configure identity server with in-memory stores, keys, clients and scopes
services.AddIdentityServer()
    .AddDeveloperSigningCredential()
    .AddTestUsers(Config.GetUsers())
    // this adds the config data from DB (clients, resources)
    .AddConfigurationStore(options =>
    {
        options.ConfigureDbContext = builder =>
            builder.UseSqlServer(connectionString,
                sql => sql.MigrationsAssembly(migrationsAssembly));
    })
    // this adds the operational data from DB (codes, tokens, consents)
    .AddOperationalStore(options =>
    {
        options.ConfigureDbContext = builder =>
            builder.UseSqlServer(connectionString,
                sql => sql.MigrationsAssembly(migrationsAssembly));

        // this enables automatic token cleanup. this is optional.
        options.EnableTokenCleanup = true;
        options.TokenCleanupInterval = 30;
    });

您可能须要将这些命名空间添加到文件中:

using Microsoft.EntityFrameworkCore;
using System.Reflection;

上面的代码是对链接字符串进行硬编码的,若是你愿意,你能够随意更改。 此外,对AddConfigurationStore和AddOperationalStore的调用正在注册EF支持的存储实现。

添加迁移

要建立迁移,请在IdentityServer项目目录中打开命令提示符。 在命令提示符下运行这两个命令:

dotnet ef migrations add InitialIdentityServerPersistedGrantDbMigration -c PersistedGrantDbContext -o Data/Migrations/IdentityServer/PersistedGrantDb
dotnet ef migrations add InitialIdentityServerConfigurationDbMigration -c ConfigurationDbContext -o Data/Migrations/IdentityServer/ConfigurationDb

您如今应该在项目中看到一个〜/Data/Migrations/IdentityServer文件夹。 这包含新建立的迁移的代码。

初始化数据库

如今咱们已经进行了迁移,咱们能够编写代码来从迁移中建立数据库。 咱们还将使用咱们在以前的快速入门中定义的内存配置数据对数据库进行种子处理。

在Startup.cs中添加此方法以帮助初始化数据库:

private void InitializeDatabase(IApplicationBuilder app)
{
    using (var serviceScope = app.ApplicationServices.GetService<IServiceScopeFactory>().CreateScope())
    {
        serviceScope.ServiceProvider.GetRequiredService<PersistedGrantDbContext>().Database.Migrate();

        var context = serviceScope.ServiceProvider.GetRequiredService<ConfigurationDbContext>();
        context.Database.Migrate();
        if (!context.Clients.Any())
        {
            foreach (var client in Config.GetClients())
            {
                context.Clients.Add(client.ToEntity());
            }
            context.SaveChanges();
        }

        if (!context.IdentityResources.Any())
        {
            foreach (var resource in Config.GetIdentityResources())
            {
                context.IdentityResources.Add(resource.ToEntity());
            }
            context.SaveChanges();
        }

        if (!context.ApiResources.Any())
        {
            foreach (var resource in Config.GetApiResources())
            {
                context.ApiResources.Add(resource.ToEntity());
            }
            context.SaveChanges();
        }
    }
}

而后咱们能够从Configure方法调用它::

public void Configure(IApplicationBuilder app, IHostingEnvironment env, ILoggerFactory loggerFactory)
{
    // this will do the initial DB population
    InitializeDatabase(app);

    // the rest of the code that was already here
    // ...
}

上面的InitializeDatabase帮助程序API能够方便地为数据库建立种子,但这种方法并不适合每次运行应用程序时执行。 一旦你的数据库被填充,考虑删除对API的调用。

运行客户端应用程序

您如今应该可以运行任何现有的客户端应用程序并登陆,获取令牌并调用API - 所有基于数据库配置。

 

 案例下载:https://pan.baidu.com/s/1zVRXIEB9VMbbbCBf3kokTA

10、社区快速入门&样品

各类ASP.NET core安全示例

https://github.com/leastprivilege/AspNetCoreSecuritySamples

IdentityServer4 EF 和 ASP.NET Identity

这个样本结合了EF和ASP.NET身份快速入门(#6和#8)。

共同托管IdentityServer4和一个Web API

此示例显示如何在保护API的IdentityServer所在的主机上托管API。

https://github.com/brockallen/IdentityServerAndApi

用于MongoDB的IdentityServer4案例

  • IdentityServer4-mongo:与快速入门#8 EntityFramework配置相似,但使用MongoDB做为配置数据。 
  • IdentityServer4-mongo-AspIdentity:更详细的示例基于使用ASP.NET身份进行身份管理,该身份管理使用MongoDB做为配置数据

https://github.com/souzartn/IdentityServer4.Samples.Mongo

从Facebook,Google和Twitter交换外部令牌

演示如何使用扩展受权将外部身份验证令牌交换到身份服务器访问令牌

https://github.com/waqaskhan540/IdentityServerExternalAuth

IdentityServer4快速入门UI的ASP.NET Core MVC RazorPages模板

Razor Pages based QuickStart sample by Martin Fletcher.

.NET core和ASP.NET core“平台”方案

显示受信任的“内部”应用程序与“外部”应用程序与.NET Core 2.0和ASP.NET Core 2.0应用程序的交互

https://github.com/BenjaminAbt/Samples.AspNetCore-IdentityServer4

使用JWKS使用来自IdentityServer4的令牌保护Node API

  • 演示如何使用IdentityServer4的JWKS端点和RS256算法来保护Node(Express)API。
  • 使用更高质量的生产准备模块为IdentityServer4.Samples中的NodeJsApi样本提供备选方案。

https://github.com/lyphtec/idsvr4-node-jwks