IdentityServer4 实现 OpenID Connect 和 OAuth 2.0

2023-05-16

关于 OAuth 2.0 的相关内容,点击查看:ASP.NET WebApi OWIN 实现 OAuth 2.0

OpenID 是一个去中心化的网上身份认证系统。对于支持 OpenID 的网站,用户不需要记住像用户名和密码这样的传统验证标记。取而代之的是,他们只需要预先在一个作为 OpenID 身份提供者(identity provider, IdP)的网站上注册。OpenID 是去中心化的,任何网站都可以使用 OpenID 来作为用户登录的一种方式,任何网站也都可以作为 OpenID 身份提供者。OpenID 既解决了问题而又不需要依赖于中心性的网站来确认数字身份。

OpenID 相关基本术语:

  • 最终用户(End User):想要向某个网站表明身份的人。
  • 标识(Identifier):最终用户用以标识其身份的 URL 或 XRI。
  • 身份提供者(Identity Provider, IdP):提供 OpenID URL 或 XRI 注册和验证服务的服务提供者。
  • 依赖方(Relying Party, RP):想要对最终用户的标识进行验证的网站。

以上概念来自:https://zh.wikipedia.org/wiki/OpenID

针对 .NET Core 跨平台,微软官方并没有针对 OAuth 2.0 的实现(Microsoft.AspNetCore.Authentication.OAuth组件,仅限客户端),IdentityServer4 实现了 ASP.NET Core 下的 OpenID Connect 和 OAuth 2.0,IdentityServer4 也是微软基金会成员。

阅读目录:

  • OpenID 和 OAuth 的区别
  • 客户端模式(Client Credentials)
  • 密码模式(resource owner password credentials)
  • 简化模式-With OpenID(implicit grant type)
  • 简化模式-With OpenID & OAuth(JS 客户端调用)
  • 混合模式-With OpenID & OAuth(Hybrid Flow)
  • ASP.NET Core Identity and Using EntityFramework Core for configuration data

开源地址:https://github.com/yuezhongxin/IdentityServer4.Demo

1. OpenID 和 OAuth 的区别

简单概括:

  • OpenID:authentication(认证),用户是谁?
  • OAuth:authorization(授权),用户能做什么?

其实,OAuth 的密码授权模式和 OpenID 有些类似,但也不相同,比如用户登录落网选择微博快捷登录方式,大致的区别:

  • OAuth:用户在微博授权页面输入微博的账号和密码,微博验证成功之后,返回 access_token,然后落网拿到 access_token 之后,再去请求微博的用户 API,微博授权中心验证 access_token,如果验证通过,则返回用户 API 的请求数据给落网。
  • OpenID:落网可以没有用户的任何实现,落网需要确认一个 URL 标识(可以是多个),然后用户登录的时候,选择一个 URL 进行登录(比如微博),跳转到微博 OpenID 登录页面,用户输入微博的账号和密码,微博验证成功之后,按照用户的选择,返回用户的一些信息。

可以看到,OAuth 首先需要拿到一个授权(access_token),然后再通过这个授权,去资源服务器(具体的 API),获取想要的一些数据,上面示例中,用户 API 只是资源服务器的一种(可以是视频 API、文章 API 等等),在这个过程中,OAuth 最重要的就是获取授权(四种模式),获取到授权之后,你就可以通过这个授权,做授权范围之类的任何事了。

而对于 OpenID 来说,授权和它没任何关系,它只关心的是用户,比如落网,可以不进行用户的任何实现(具体体现就是数据库没有 User 表),然后使用支持 OpenID 的服务(比如微博),通过特定的 URL 标识(可以看作是 OpenID 标识),然后输入提供服务的账号和密码,返回具体的用户信息,对于落网来说,它关心的是用户信息,仅此而已。

435188-20170111105252166-1157332397.png

上面其实是 OAuth 的授权,所以会有“获得以下权限”提示,如果是 OpenID 的话,“权限”应该改为“用户信息”。

支持 OpenID 的服务列表:http://openid.net/get-an-openid/

OpenID 流程图(来自 Using OpenID):

435188-20170111120202150-598990342.gif

2. 客户端模式(Client Credentials)

简单概述:客户端提供 ClientId 和 ClientSecret 给认证授权服务,验证如果成功,返回 access_token,客户端拿到 access_token,访问 API 资源服务。

2.1 认证授权服务配置

创建 ASP.NET Core 站点,Startup 配置修改如下:

public class Startup
{
    public void ConfigureServices(IServiceCollection services)
    {
        // configure identity server with in-memory stores, keys, clients and scopes
        services.AddIdentityServer()
            .AddTemporarySigningCredential()
            .AddInMemoryApiResources(new List<ApiResource>
            {
                new ApiResource("api1", "My API")
            })
            .AddInMemoryClients(new List<Client>
            {
                // client credentials client
                new Client
                {
                    ClientId = "client",
                    AllowedGrantTypes = GrantTypes.ClientCredentials,

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

    public void Configure(IApplicationBuilder app, ILoggerFactory loggerFactory)
    {
        loggerFactory.AddConsole(LogLevel.Debug);
        app.UseDeveloperExceptionPage();

        app.UseIdentityServer();
    }
}

IdentityServer4 中AddInMemory的相关配置,都是 Mock 的(代码配置),也可以把这些配置存储在数据库中,这个后面再讲。

AddInMemoryApiResources 增加的 API 资源服务(List 集合),也就此认证授权服务所管辖的 API 资源,比如上面配置的 api1,这个会在客户端调用的时候用到,如果不一致,是不允许访问的,另外,Clinet 中配置的AllowedScopes = { "api1" },表示此种授权模式允许的 API 资源集合(前提是需要添加ApiResource)。

配置很简单,我们也可以访问http://localhost:5000/.well-known/openid-configuration,查看具体的配置信息:

435188-20170111143441791-1201724230.png

2.2 API 资源服务配置

API 资源服务站点,需要添加程序包:

"IdentityServer4.AccessTokenValidation": "1.0.1"

添加一个ValuesController

[Route("[controller]")]
[Authorize]
public class ValuesController : ControllerBase
{
    [HttpGet]
    public IActionResult Get()
    {
        return Content("hello world");
    }
}

2.3 单元测试

需要添加程序包:

"IdentityModel": "2.0.0"

单元测试代码:

[Fact]
public async Task ClientCredentials_Test()
{
    // request token
    var disco = await DiscoveryClient.GetAsync("http://localhost:5000");
    var tokenClient = new TokenClient(disco.TokenEndpoint, "client", "secret");
    var tokenResponse = await tokenClient.RequestClientCredentialsAsync("api1");

    Assert.False(tokenResponse.IsError);
    Console.WriteLine(tokenResponse.Json);

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

    var response = await client.GetAsync("http://localhost:5010/values");
    Assert.True(response.IsSuccessStatusCode);
    var content = await response.Content.ReadAsStringAsync();
    Console.WriteLine(content);
}

很简单,和我们之前用 ASP.NET WebApi OWIN 实现 OAuth 2.0 一样,只不过配置和调用简化了很多,因为 IdentityServer4 替我们做了很多工作。

3. 密码模式(resource owner password credentials)

简单概述:客户端提供 UserName 和 Password 给认证授权服务,验证如果成功,返回 access_token,客户端拿到 access_token,访问 API 资源服务。

3.1 认证授权服务配置

创建 ASP.NET Core 站点,Startup 配置修改如下:

public class Startup
{
    public void ConfigureServices(IServiceCollection services)
    {
        // configure identity server with in-memory stores, keys, clients and scopes
        services.AddIdentityServer()
            .AddTemporarySigningCredential()
            .AddInMemoryApiResources(new List<ApiResource>
            {
                new ApiResource("api1", "My API")
            })
            .AddInMemoryClients(new List<Client>
            {
                // resource owner password grant client
                new Client
                {
                    ClientId = "ro.client",
                    AllowedGrantTypes = GrantTypes.ResourceOwnerPassword,

                    ClientSecrets =
                    {
                        new Secret("secret".Sha256())
                    },
                    AllowedScopes = { "api1" }
                }
            })
            .AddTestUsers(new List<TestUser>
            {
                new TestUser
                {
                    SubjectId = "1",
                    Username = "xishuai",
                    Password = "123"
                }
            });
    }

    public void Configure(IApplicationBuilder app, ILoggerFactory loggerFactory)
    {
        loggerFactory.AddConsole(LogLevel.Debug);
        app.UseDeveloperExceptionPage();

        app.UseIdentityServer();
    }
}

和客户端模式不同的是,AllowedGrantTypes授权模式改为了ResourceOwnerPassword,然后增加了测试用户(用来验证用户名和密码),也可以存储在数据库中。

3.2 API 资源服务配置

API 资源服务站点,需要添加程序包:

"IdentityServer4.AccessTokenValidation": "1.0.1"

添加一个IdentityController

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

3.3 单元测试

需要添加程序包:

"IdentityModel": "2.0.0"

单元测试代码:

[Fact]
public async Task ResourceOwnerPassword_Test()
{
    // request token
    var disco = await DiscoveryClient.GetAsync("http://localhost:5000");
    var tokenClient = new TokenClient(disco.TokenEndpoint, "ro.client", "secret");
    var tokenResponse = await tokenClient.RequestResourceOwnerPasswordAsync("xishuai", "123", "api1");

    Assert.False(tokenResponse.IsError);
    Console.WriteLine(tokenResponse.Json);

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

    var response = await client.GetAsync("http://localhost:5010/identity");
    Assert.True(response.IsSuccessStatusCode);
    var content = await response.Content.ReadAsStringAsync();
    Console.WriteLine(JArray.Parse(content));
}

4. 简化模式-With OpenID(implicit grant type)

简化模式在 IdentityServer4 中的实现,就是 OpenID Connect。

简单概述:客户端确定 URL(用户认证服务),登录在用户认证服务,验证成功,返回客户端想要的用户数据,并使此用户为登录状态,可以在客户端进行注销用户。

4.1 认证授权服务配置

创建 ASP.NET Core 站点,Startup 配置修改如下:

public class Startup
{
    public void ConfigureServices(IServiceCollection services)
    {
        // configure identity server with in-memory stores, keys, clients and scopes
        services.AddIdentityServer()
            .AddTemporarySigningCredential()
            .AddInMemoryIdentityResources(new List<IdentityResource>
            {
                new IdentityResources.OpenId(),
                new IdentityResources.Profile(),
            })
            .AddInMemoryClients(new List<Client>
            {
                // OpenID Connect implicit flow client (MVC)
                new Client
                {
                    ClientId = "mvc",
                    ClientName = "MVC Client",
                    AllowedGrantTypes = GrantTypes.Implicit,

                    RedirectUris = { "http://localhost:5020/signin-oidc" },
                    PostLogoutRedirectUris = { "http://localhost:5020" },

                    AllowedScopes =
                    {
                        IdentityServerConstants.StandardScopes.OpenId,
                        IdentityServerConstants.StandardScopes.Profile
                    }
                }
            })
            .AddTestUsers(new List<TestUser>
            {
                new TestUser
                {
                    SubjectId = "1",
                    Username = "xishuai",
                    Password = "123",
                    Claims = new List<Claim>
                    {
                        new Claim("name", "xishuai"),
                        new Claim("website", "http://xishuai.cnblogs.com")
                    }
                }
            });
    }

    public void Configure(IApplicationBuilder app, ILoggerFactory loggerFactory)
    {
        loggerFactory.AddConsole(LogLevel.Debug);
        app.UseDeveloperExceptionPage();

        app.UseIdentityServer();
    }
}

AddInMemoryIdentityResourcesAllowedScopes所配置的,是客户端允许访问的用户信息,具体查看:Requesting Claims using Scope Values

ClientId 很重要,必须和客户端一一对应,所以想要使用 OpenID 认证服务的客户端,需要向提供 OpenID 认证服务的机构,申请一个 ClientId,OpenID 认证服务会统一发放一个用户登录的 URL。

TestUser中的Claims配置,其实就是IdentityServerConstants.StandardScopes.Profile

另外,还有用户登录的一些操作代码,这边就不贴了,可以查看具体的实现:ImplicitServer.Web

4.2 客户端服务配置

创建 ASP.NET Core 站点,添加程序包:

"Microsoft.AspNetCore.Authentication.Cookies": "1.0.*",
"Microsoft.AspNetCore.Authentication.OpenIdConnect": "1.0.*"

Startup 配置修改如下:

public Startup(IHostingEnvironment env)
{
    var builder = new ConfigurationBuilder()
        .SetBasePath(env.ContentRootPath)
        .AddJsonFile("appsettings.json", optional: true, reloadOnChange: true)
        .AddJsonFile($"appsettings.{env.EnvironmentName}.json", optional: true)
        .AddEnvironmentVariables();

    Configuration = builder.Build();
}

public IConfigurationRoot Configuration { get; }

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

public void Configure(IApplicationBuilder app, IHostingEnvironment env, ILoggerFactory loggerFactory)
{
    JwtSecurityTokenHandler.DefaultInboundClaimTypeMap.Clear();

    loggerFactory.AddConsole(Configuration.GetSection("Logging"));
    loggerFactory.AddDebug();

    if (env.IsDevelopment())
    {
        app.UseDeveloperExceptionPage();
    }
    else
    {
        app.UseExceptionHandler("/Home/Error");
    }

    app.UseCookieAuthentication(new CookieAuthenticationOptions
    {
        AuthenticationScheme = "Cookies"
    });

    app.UseOpenIdConnectAuthentication(new OpenIdConnectOptions
    {
        AuthenticationScheme = "oidc",
        SignInScheme = "Cookies",

        Authority = "http://localhost:5001",
        RequireHttpsMetadata = false,

        ClientId = "mvc",
        SaveTokens = true
    });
    
    app.UseStaticFiles();
    app.UseMvcWithDefaultRoute();
}

UseOpenIdConnectAuthentication配置中的Authority,就是 OpenID 认证服务的 URL。

添加一个HomeController

public class HomeController : Controller
{
    public IActionResult Index()
    {
        return View();
    }

    [Authorize]
    public IActionResult Secure()
    {
        ViewData["Message"] = "Secure page.";

        return View();
    }

    public async Task Logout()
    {
        await HttpContext.Authentication.SignOutAsync("Cookies");
        await HttpContext.Authentication.SignOutAsync("oidc");
    }

    public IActionResult Error()
    {
        return View();
    }
}

访问 Secure 页面,跳转到认证服务地址,进行账号密码登录,Logout 用于用户的注销操作。

4.3 Web 测试

435188-20170111155803103-1209138568.gif

5. 简化模式-With OpenID & OAuth(JS 客户端调用)

简单概述:客户端确定 URL(用户认证服务),登录在用户认证服务,验证成功,返回客户端想要的用户数据 和 access_token,并使此用户为登录状态,可以在客户端进行注销用户,客户端可以拿到 access_token,去访问授权范围之内的 API 资源。

需要注意的是:因为简化模式,所以 access_token 是作为 URL 参数返回的。

5.1 认证授权服务配置

创建 ASP.NET Core 站点,Startup 配置修改如下:

public class Startup
{
    public void ConfigureServices(IServiceCollection services)
    {
        // configure identity server with in-memory stores, keys, clients and scopes
        services.AddIdentityServer()
            .AddTemporarySigningCredential()
            .AddInMemoryIdentityResources(new List<IdentityResource>
            {
                new IdentityResources.OpenId(),
                new IdentityResources.Profile(),
            })
            .AddInMemoryApiResources(new List<ApiResource>
            {
                new ApiResource("api1", "My API")
            })
            .AddInMemoryClients(new List<Client>
            {
                // OpenID Connect implicit flow client (MVC)
                new Client
                {
                    ClientId = "js",
                    ClientName = "JavaScript Client",
                    AllowedGrantTypes = GrantTypes.Implicit,
                    AllowAccessTokensViaBrowser = true,

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

                    RequireConsent = false, //禁用 consent 页面确认 https://github.com/IdentityServer/IdentityServer3/issues/863

                    AllowedScopes =
                    {
                        IdentityServerConstants.StandardScopes.OpenId,
                        IdentityServerConstants.StandardScopes.Profile,
                        "api1"
                    }
                }
            })
            .AddTestUsers(new List<TestUser>
            {
                new TestUser
                {
                    SubjectId = "1",
                    Username = "xishuai",
                    Password = "123",
                    Claims = new List<Claim>
                    {
                        new Claim("name", "xishuai"),
                        new Claim("website", "http://xishuai.cnblogs.com")
                    }
                }
            });
    }

    public void Configure(IApplicationBuilder app, ILoggerFactory loggerFactory)
    {
        loggerFactory.AddConsole(LogLevel.Debug);
        app.UseDeveloperExceptionPage();

        app.UseIdentityServer();
    }
}

因为涉及到访问 API 资源操作,需要需要添加AddInMemoryApiResources配置,AllowedScopes也需要添加对应的 API 资源名称,AllowAccessTokensViaBrowser = true的配置的作用就是,可以在浏览器地址中访问 access_token。

更多实现代码,点击查看:ImplicitServerWithJS.Web

5.2 API 资源服务配置

API 资源服务站点,需要添加程序包:

"IdentityServer4.AccessTokenValidation": "1.0.1",
"Microsoft.AspNetCore.Cors": "1.1.0"

Startup 配置修改如下:

public Startup(IHostingEnvironment env)
{
    var builder = new ConfigurationBuilder()
        .SetBasePath(env.ContentRootPath)
        .AddJsonFile("appsettings.json", optional: true, reloadOnChange: true)
        .AddJsonFile($"appsettings.{env.EnvironmentName}.json", optional: true);

    builder.AddEnvironmentVariables();
    Configuration = builder.Build();
}

public IConfigurationRoot Configuration { get; }

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

    services.AddMvcCore()
        .AddAuthorization()
        .AddJsonFormatters();
}

public void Configure(IApplicationBuilder app, ILoggerFactory loggerFactory)
{
    loggerFactory.AddConsole(Configuration.GetSection("Logging"));
    loggerFactory.AddDebug();

    app.UseCors("default");

    app.UseIdentityServerAuthentication(new IdentityServerAuthenticationOptions
    {
        Authority = "http://localhost:5003",
        RequireHttpsMetadata = false,

        ApiName = "api1"
    });

    app.UseMvc();
}

因为 JS 需要跨域访问 API 资源服务,所以需要增加 CORS 配置。

添加一个IdentityController

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

5.3 JS Web 站点测试

创建一个 ASP.NET Core 站点,添加oidc-client.js前端组件,测试 JS 代码:

/// <reference path="oidc-client.js" />

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';
    });
}

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

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

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

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

function api() {
    mgr.getUser().then(function (user) {
        var url = "http://localhost:5012/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();
}

测试过程(注意下 URL 中的参数):

435188-20170111164420510-1695158064.gif

6. 混合模式-With OpenID & OAuth(Hybrid Flow)

混合模式(Hybrid Flow)是一种新的模式,是简化模式(implicit flow)和验证码模式(authorization code flow)的混合。

简单概述:客户端确定 URL(用户认证服务),登录在用户认证服务,验证成功,返回客户端想要的用户数据 和 access_token,并使此用户为登录状态,可以在客户端进行注销用户,客户端可以拿到 access_token,去访问授权范围之内的 API 资源。

和上面的简化模式流程差不多,不过 access_token 不是通过浏览器获取的,而是通过后台服务获取。

6.1 认证授权服务配置

创建 ASP.NET Core 站点,Startup 配置修改如下:

public class Startup
{
    public void ConfigureServices(IServiceCollection services)
    {
        // configure identity server with in-memory stores, keys, clients and scopes
        services.AddIdentityServer()
            .AddTemporarySigningCredential()
            .AddInMemoryIdentityResources(new List<IdentityResource>
            {
                new IdentityResources.OpenId(),
                new IdentityResources.Profile(),
            })
            .AddInMemoryApiResources(new List<ApiResource>
            {
                new ApiResource("api1", "My API")
            })
            .AddInMemoryClients(new List<Client>
            {
                // OpenID Connect implicit flow client (MVC)
                new Client
                {
                    ClientId = "mvc",
                    ClientName = "MVC Client",
                    AllowedGrantTypes = GrantTypes.HybridAndClientCredentials,

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

                    RedirectUris = { "http://localhost:5021/signin-oidc" },
                    PostLogoutRedirectUris = { "http://localhost:5021" },

                    AllowedScopes =
                    {
                        IdentityServerConstants.StandardScopes.OpenId,
                        IdentityServerConstants.StandardScopes.Profile,
                        "api1"
                    },
                    AllowOfflineAccess = true
                }
            })
            .AddTestUsers(new List<TestUser>
            {
                new TestUser
                {
                    SubjectId = "1",
                    Username = "xishuai",
                    Password = "123",
                    Claims = new List<Claim>
                    {
                        new Claim("name", "xishuai"),
                        new Claim("website", "http://xishuai.cnblogs.com")
                    }
                }
            });
    }

    public void Configure(IApplicationBuilder app, ILoggerFactory loggerFactory)
    {
        loggerFactory.AddConsole(LogLevel.Debug);
        app.UseDeveloperExceptionPage();

        app.UseIdentityServer();
    }
}

AllowedGrantTypes配置改为HybridAndClientCredentialsAllowOfflineAccess需要设置为true

更多实现代码,点击查看:HybridServer.Web

6.2 API 资源服务配置

API 资源服务站点,需要添加程序包:

"IdentityServer4.AccessTokenValidation": "1.0.1"

Startup 配置修改如下:

public Startup(IHostingEnvironment env)
{
    var builder = new ConfigurationBuilder()
        .SetBasePath(env.ContentRootPath)
        .AddJsonFile("appsettings.json", optional: true, reloadOnChange: true)
        .AddJsonFile($"appsettings.{env.EnvironmentName}.json", optional: true);

    builder.AddEnvironmentVariables();
    Configuration = builder.Build();
}

public IConfigurationRoot Configuration { get; }

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

public void Configure(IApplicationBuilder app, ILoggerFactory loggerFactory)
{
    loggerFactory.AddConsole(Configuration.GetSection("Logging"));
    loggerFactory.AddDebug();

    app.UseIdentityServerAuthentication(new IdentityServerAuthenticationOptions
    {
        Authority = "http://localhost:5002",
        RequireHttpsMetadata = false,

        ApiName = "api1"
    });

    app.UseMvc();
}

添加一个IdentityController

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

6.3 客户端服务配置

创建 ASP.NET Core 站点,添加程序包:

"Microsoft.AspNetCore.Authentication.Cookies": "1.0.*",
"Microsoft.AspNetCore.Authentication.OpenIdConnect": "1.0.*",
"IdentityModel": "2.0.0"

Startup 配置修改如下:

public Startup(IHostingEnvironment env)
{
    var builder = new ConfigurationBuilder()
        .SetBasePath(env.ContentRootPath)
        .AddJsonFile("appsettings.json", optional: true, reloadOnChange: true)
        .AddJsonFile($"appsettings.{env.EnvironmentName}.json", optional: true)
        .AddEnvironmentVariables();

    Configuration = builder.Build();
}

public IConfigurationRoot Configuration { get; }

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

public void Configure(IApplicationBuilder app, IHostingEnvironment env, ILoggerFactory loggerFactory)
{
    JwtSecurityTokenHandler.DefaultInboundClaimTypeMap.Clear();

    loggerFactory.AddConsole(Configuration.GetSection("Logging"));
    loggerFactory.AddDebug();

    if (env.IsDevelopment())
    {
        app.UseDeveloperExceptionPage();
    }
    else
    {
        app.UseExceptionHandler("/Home/Error");
    }

    app.UseCookieAuthentication(new CookieAuthenticationOptions
    {
        AuthenticationScheme = "Cookies"
    });

    app.UseOpenIdConnectAuthentication(new OpenIdConnectOptions
    {
        AuthenticationScheme = "oidc",
        SignInScheme = "Cookies",

        Authority = "http://localhost:5002",
        RequireHttpsMetadata = false,

        ClientId = "mvc",
        ClientSecret = "secret",

        ResponseType = "code id_token",
        Scope = { "api1", "offline_access" },

        GetClaimsFromUserInfoEndpoint = true,
        SaveTokens = true
    });

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

添加一个HomeController

public class HomeController : Controller
{
    public IActionResult Index()
    {
        return View();
    }

    [Authorize]
    public IActionResult Secure()
    {
        ViewData["Message"] = "Secure page.";

        return View();
    }

    public async Task Logout()
    {
        await HttpContext.Authentication.SignOutAsync("Cookies");
        await HttpContext.Authentication.SignOutAsync("oidc");
    }

    public IActionResult Error()
    {
        return View();
    }

    public async Task<IActionResult> CallApiUsingClientCredentials()
    {
        var tokenClient = new TokenClient("http://localhost:5002/connect/token", "mvc", "secret");
        var tokenResponse = await tokenClient.RequestClientCredentialsAsync("api1");

        var client = new HttpClient();
        client.SetBearerToken(tokenResponse.AccessToken);
        var content = await client.GetStringAsync("http://localhost:5011/identity");

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

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

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

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

CallApiUsingClientCredentials是通过客户端模式获取 access_token,CallApiUsingUserAccessToken是通过上下文获取保存的 access_token,其实和浏览器 URL 中获取是一样的意思,但需要配置SaveTokens = true

6.4 Web 测试

435188-20170111180830806-1510056788.gif

7. ASP.NET Core Identity and Using EntityFramework Core for configuration data

使用 ASP.NET Core Identity,就是用户管理不由 OpenID 认证服务进行提供,ASP.NET Core Identity 就相当于用户的一个管理者,比如用户的存储等。

我没做这一块的示例,配置比较简单:

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

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

    services.AddMvc();

    services.AddTransient<IEmailSender, AuthMessageSender>();
    services.AddTransient<ISmsSender, AuthMessageSender>();

    // Adds IdentityServer
    services.AddIdentityServer()
        .AddTemporarySigningCredential()
        .AddInMemoryIdentityResources(Config.GetIdentityResources())
        .AddInMemoryApiResources(Config.GetApiResources())
        .AddInMemoryClients(Config.GetClients())
        .AddAspNetIdentity<ApplicationUser>();
}

详细使用:Using ASP.NET Core Identity

关于 IdentityServer4 的配置信息,可以使用 EntityFramework Core 进行存储,配置如下:

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

    var connectionString = @"server=(localdb)\mssqllocaldb;database=IdentityServer4.Quickstart;trusted_connection=yes";
    var migrationsAssembly = typeof(Startup).GetTypeInfo().Assembly.GetName().Name;

    // configure identity server with in-memory users, but EF stores for clients and resources
    services.AddIdentityServer()
        .AddTemporarySigningCredential()
        .AddTestUsers(Config.GetUsers())
        .AddConfigurationStore(builder =>
            builder.UseSqlServer(connectionString, options =>
                options.MigrationsAssembly(migrationsAssembly)))
        .AddOperationalStore(builder =>
            builder.UseSqlServer(connectionString, options =>
                options.MigrationsAssembly(migrationsAssembly)));
}

详细使用:Using EntityFramework Core for configuration data


最后,简要总结下使用 IdentityServer4 的几种应用场景:

  • 客户端模式(Client Credentials):和用户无关,用于应用程序与 API 资源的直接交互场景。
  • 密码模式(resource owner password credentials):和用户有关,一般用于第三方登录。
  • 简化模式-With OpenID(implicit grant type):仅限 OpenID 认证服务,用于第三方用户登录及获取用户信息,不包含授权。
  • 简化模式-With OpenID & OAuth(JS 客户端调用):包含 OpenID 认证服务和 OAuth 授权,但只针对 JS 调用(URL 参数获取),一般用于前端或无线端。
  • 混合模式-With OpenID & OAuth(Hybrid Flow):推荐使用,包含 OpenID 认证服务和 OAuth 授权,但针对的是后端服务调用。

开源地址:https://github.com/yuezhongxin/IdentityServer4.Demo

本文转自田园里的蟋蟀博客园博客,原文链接:http://www.cnblogs.com/xishuai/p/identityserver4-implement-openid-connect-and-oauth2.html,如需转载请自行联系原作者

本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

IdentityServer4 实现 OpenID Connect 和 OAuth 2.0 的相关文章

随机推荐

  • 适合初学者小白的10本Python书籍,你值得收藏

    我会经常分享一本书 你看完如果对你有帮助 xff0c 值得你购买 xff0c 请到官网购买正版书籍 声明 xff1a 我不是卖书的 xff0c 我搞得是Python技术 xff0c 文章最后免费为你准备了一些Python资料 Python是
  • Rust开发操作系统系列:从零制作x86_64位系统

    Rust开发操作系统系列 xff1a 从零制作x86 64位系统 在发表这个文章之前 xff0c 我曾发布过另一篇文章 xff1a Rust开发操作系统系列 xff1a 全新Hello World系统 那篇文章我发布在云栖社区以及我自己的博
  • 莫名其妙报module 'urllib' has no attribute 'request'的解决方法

    2019独角兽企业重金招聘Python工程师标准 gt gt gt 以前在pycharm下运行好好的程序 xff0c 今天就加了两个log xff0c 莫名其妙报错说urllib下找不到request 报错 xff1a module 39
  • 【NativeScript Vue 2019--中文字幕】精通原生手机UI之路

    在NativeScript原生手机app开发里 xff0c 其中的UI界面就跟我们常用的html 或blade模板 或vue的普通template不一样了 xff0c 其中就有很多值得警惕的了 xff0c 有的方式是好的 xff0c 可以提
  • SpringBoot整合阿里云OSS文件上传、下载、查看、删除

    该项目源码地址 xff1a https github com ggb2312 JavaNotes tree master springboot integration examples xff08 其中包含SpringBoot和其他常用技术
  • sql server 对储存过程和函数的加密解密

    加密使用关键字 xff1a ENCRYPTION CREATE procedure dbo sp EytTest 64 para varchar 50 WITH ENCRYPTION as begin select 64 para end
  • 设计模式之(十三)备忘机制Memento

    Memento备望录模式定义 memento是一个保存另外一个对象内部状态拷贝的对象 这样以后就可以将该对象恢复到原先保存的状态 Memento模式相对也比较好理解 我们看下列代码 public class Originator publi
  • 从点击Button到弹出一个MessageBox, 背后发生了什么

    思考一个最简单的程序行为 xff1a 我们的Dialog上有一个Button xff0c 当用户用鼠标点击这个Button时 xff0c 我们弹出一个MessageBox 这个看似简单的行为 xff0c 谁能说清楚它是如何运行起来的 xff
  • Windows 7 封装篇(一)【母盘定制】[手动制作]定制合适的系统母盘

    Windows 7 封装篇 xff08 一 xff09 母盘定制 手动制作 定制合适的系统母盘 http www win10u com article html 10 html Windows 7 封装篇 xff08 一 xff09 母盘定
  • #ifdef __cplusplus 倒底是什么意思?

    时常在cpp的代码之中看到这样的代码 ifdef cplusplus extern 34 C 34 endif 一段代码 ifdef cplusplus endif 这样的代码到底是什么意思呢 xff1f 首先 xff0c cplusplu
  • ping过程详细解读

    0 前言 在讲解ping过程之前 xff0c 我们需要了解以下概念 1 何为ping PING Packet Internet Groper xff0c 因特网包探索器 xff0c 用于测试网络连通性的程序 Ping发送一个ICMP Int
  • 使用源码安装MariaDB

    2019独角兽企业重金招聘Python工程师标准 gt gt gt 系统 xff1a CentOS 6 5 x86 64 为什么源码安装 xff1f 自定义编译参数 xff0c 编译器优化 xff0c 自定义安装位置 源码安装系统需求 xf
  • VS2015配置Linux开发远程调试

    VS2015配置Linux开发远程调试 简介 vs2015支持跨平台开发 软件环境 VS2015 update3 Visual C 43 43 for Linux Development VC Linux exe 安装步骤 安装VS2015
  • 将linux文件中的tab更换为空格的三种方法

    将linux文件中的tab更换为空格的三种方法 1 xff0c 用sed命令 sed s t g filename gt filename1 2 用tr命令 cat filename tr 34 t 34 34 34 gt filename
  • React生命周期-踩坑记_10

    React生命周期 生命周期概览 生命周期的状态 组件的生命周期可分成三个状态 xff1a Mounting xff1a 已插入真实 DOMUpdating xff1a 正在被重新渲Unmounting xff1a 已移出真实 DOMcom
  • 转录组入门(5): 序列比对

    欢迎来GitHub上fork xff0c 一起进步 xff1a https github com xuzhougeng 比对软件很多 xff0c 首先大家去收集一下 xff0c 因为我们是带大家入门 xff0c 请统一用hisat2 xff
  • amavisd 规则备忘

    邮件服务器在做内容过滤 xff0c 结果误杀好多邮件 xff0c 有一些常用规则再现 xff0c 记录一下 20150113 初版 amavisd 版本为2 6 4 https wiki apache org spamassassin Ru
  • TensorFlow Lite 支持移动 GPU,速度推测提升4-6倍

    最近 xff0c 开源机器学习框架 TensorFlow Lite 更新 xff0c 新版更新支持了 GPU 支持 GPU 原因 虽然移动设备的处理能力和功率都有限 虽然 TensorFlow Lite 提供了不少的加速途径 xff0c 比
  • Linux开机启动顺序简述

    Linux的开机启动顺序 第一步 xff1a 当然是加电了 第二步 xff1a 加载BIOS设置 xff0c 选择启动盘 这是因为因为BIOS中包含了CPU的相关信息 设备启动顺序信息 硬盘信息 内存信息 时钟信息 PnP特性等等 在此之后
  • IdentityServer4 实现 OpenID Connect 和 OAuth 2.0

    关于 OAuth 2 0 的相关内容 xff0c 点击查看 xff1a ASP NET WebApi OWIN 实现 OAuth 2 0 OpenID 是一个去中心化的网上身份认证系统 对于支持 OpenID 的网站 xff0c 用户不需要