继续浏览精彩内容
慕课网APP
程序员的梦工厂
打开
继续
感谢您的支持,我会继续努力的
赞赏金额会直接到老师账户
将二维码发送给自己后长按识别
微信支付
支付宝支付

【.NET Core】ASP.NET Core之IdentityServer4(1):快速入门

回首忆惘然
关注TA
已关注
手记 342
粉丝 73
获赞 413

本文中的IdentityServer4基于上节的jenkins 进行docker自动化部署。
使用了MariaDB,EF Core,AspNetIdentity,Docker

Demo地址:https://sso.neverc.cn
Demo源码:https://github.com/NeverCL/Geek.IdentityServer4

简介

OpenID Connect :常用的认证协议有SAML2p, WS-Federation and OpenID Connect – SAML2p。OpenID Connect是其中最新的协议。

OAuth 2.0 :OAuth 2.0 是一种授权协议。通过Access Token可以访问受保护的API接口。

OpenID Connect和OAuth 2.0非常相似,实际上,OpenID Connect是OAuth 2.0之上的一个扩展。
身份认证和API访问这两个基本的安全问题被合并为一个协议 - 往往只需一次往返安全令牌服务。

IdentityServer4基于ASP.NET Core 2对这两种协议的实现。

支持规范:https://identityserver4.readthedocs.io/en/release/intro/specs.html

关键词

IdentityServer:提供OpenID Connect and OAuth 2.0 protocols.

User:IdentityServer中的用户

Client:第三方应用,包括web applications, native mobile or desktop applications, SPAs etc.

Resource:包含Identity data 和 APIs。这是认证授权中的标识。

Identity Token:标识认证信息,至少包含user的sub claim。

Access Token:标识授权信息,可以包含Client 和 user的claim信息。

授权方式

Client Credentials

Client Credentials是最简单的一种授权方式。

步骤:

  1. 创建IdentityServer

    1. 定义APIs

    2. 定义Client

  2. 创建API

    1. 定义Authentication

  3. 使用Client

    1. 请求Token

    2. 使用Token

IdentityServer:

dotnet new web -o Geek.IdentityServer4 && dotnet add Geek.IdentityServer4 package IdentityServer4

Startup:

services.AddIdentityServer()    .AddDeveloperSigningCredential()    .AddInMemoryApiResources(Config.GetApiResources())    .AddInMemoryClients(Config.GetClients());
...app.UseIdentityServer();

Config:

public static IEnumerable<ApiResource> GetApiResources(){    return new List<ApiResource>
    {        new ApiResource("api1")
    };
}public static IEnumerable<Client> GetClients(){    return new List<Client>
    {        new Client
        {
            ClientId = "client",
            AllowedGrantTypes = GrantTypes.ClientCredentials,
            ClientSecrets = { new Secret("secret".Sha256()) },
            Claims = { new Claim("name","名称") },
            AllowedScopes = { "api1" }
        },
    }
}

API:

dotnet new web -o Geek.Api && dotnet add Geek.Api package IdentityServer4.AccessTokenValidation

Startup:

services.AddMvc();
services.AddAuthentication("Bearer")//AddIdentityServerAuthentication 默认SchemeName:Bearer
    .AddIdentityServerAuthentication(opt =>
    {
        opt.ApiName = "api1";
        opt.Authority = "https://sso.neverc.cn";
    });
...
app.UseAuthentication();
app.UseMvc();

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

Client:

dotnet new web -o Geek.Client && dotnet add Geek.Client package IdentityServer4.IdentityModel

Program:

var disco = await DiscoveryClient.GetAsync("https://sso.neverc.cn");var tokenClient = new TokenClient(disco.TokenEndpoint, "client", "secret");var tokenResponse = await tokenClient.RequestClientCredentialsAsync("api1");var client = new HttpClient();
client.SetBearerToken(tokenResponse.AccessToken);var response = await client.GetAsync("http://localhost:5001/identity");var content = await response.Content.ReadAsStringAsync();
Console.WriteLine(JArray.Parse(content));

ResourceOwnerPassword

这种认证方式需要User提供用户名和密码,所以Client为非常可信的应用才可能使用这种方式。

步骤:

  1. 定义RO Client 和 User

  2. 使用Client

Identity Server

Config:

public static IEnumerable<Client> GetClients(){
    ...    new Client
    {
        ClientId = "ro.client",
        AllowedGrantTypes = GrantTypes.ResourceOwnerPassword,

        ClientSecrets = { new Secret("secret".Sha256()) },
        AllowedScopes = { "api1" }
    }
}public static List<TestUser> GetUsers(){    return new List<TestUser>
    {        new TestUser
        {
            SubjectId = "1",
            Username = "alice",
            Password = "password",
        }
    }
}

Startup:

services.AddIdentityServer()    .AddDeveloperSigningCredential()    .AddInMemoryApiResources(Config.GetApiResources())    .AddInMemoryClients(Config.GetClients())    .AddTestUsers(Config.GetUsers());

Client

var disco = await DiscoveryClient.GetAsync("https://sso.neverc.cn");var tokenClient = new TokenClient(disco.TokenEndpoint, "ro.client", "secret");var tokenResponse = await tokenClient.RequestResourceOwnerPasswordAsync("alice", "password", "api1");var client = new HttpClient();
client.SetBearerToken(tokenResponse.AccessToken);var response = await client.GetAsync("http://localhost:5001/identity");var content = await response.Content.ReadAsStringAsync();
Console.WriteLine(JArray.Parse(content));

区分Client Credentials 和 ResourceOwnerPassword 可通过 sub claim来区分

Implicit

Implicit为隐式模式,通过浏览器端直接传输id_token

步骤:

  1. 配置IdentityServer

    1. 定义IdentityResources

    2. 定义mvc client

    3. 添加Mvc UI

  2. 创建mvc client

IdentityServer

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

...new Client
{
    ClientId = "mvc",
    ClientName = "MVC Client",
    AllowedGrantTypes = GrantTypes.Implicit,
    ClientSecrets = { new Secret("secret".Sha256()) },
    RedirectUris = { "http://localhost:5002/signin-oidc" },
    PostLogoutRedirectUris = { "http://localhost:5002/signout-callback-oidc" },
    AllowedScopes = new List<string>
    {
        IdentityServerConstants.StandardScopes.OpenId,
        IdentityServerConstants.StandardScopes.Profile,
    }
}
services.AddIdentityServer()    .AddDeveloperSigningCredential()    .AddInMemoryApiResources(Config.GetApiResources())    .AddInMemoryClients(Config.GetClients())    .AddTestUsers(Config.GetUsers())    .AddInMemoryIdentityResources(Config.GetIdentityResources());

添加MvcUI:

在IdentityServer项目中powershell执行:

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

MvcClient

public void ConfigureServices(IServiceCollection services){
    JwtSecurityTokenHandler.DefaultInboundClaimTypeMap.Clear();
    services.AddMvc();
    services.AddAuthentication(options =>
    {
        options.DefaultScheme = "Cookies";
        options.DefaultChallengeScheme = "oidc";
    })
    .AddCookie("Cookies")
    .AddOpenIdConnect("oidc", options =>
    {
        options.SignInScheme = "Cookies";
        options.Authority = "https://sso.neverc.cn";
        options.ClientId = "mvc";
        options.SaveTokens = true;
    });
}public void Configure(IApplicationBuilder app, IHostingEnvironment env){
    app.UseAuthentication();
    app.UseMvcWithDefaultRoute();
}
public class HomeController : ControllerBase{
    [Authorize]    public ActionResult Index()    {        return new JsonResult(from c in User.Claims select new { c.Type, c.Value });
    }
}

Hybrid

在Implicit方式中,id_token在浏览器中传输是适用的,但是access_token不应该暴露在浏览器中。
Hybrid模式则是在Implicit的基础上,再传输code,适用code模式来获取access_token。

步骤:

  1. 定义Client

  2. 使用Client

IdentityServer配置

Config:

new Client
{
    ClientId = "hybrid",
    AllowedGrantTypes = GrantTypes.Hybrid,
    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"
    },
};

MvcClient配置

Startup:

.AddOpenIdConnect("oidc", options =>
{
    options.SignInScheme = "Cookies";
    options.Authority = "https://sso.neverc.cn";
    options.ClientId = "mvc";
    options.ClientSecret = "secret";
    options.ResponseType = "code id_token";
    options.SaveTokens = true;
    options.Scope.Add("api1");
});

Controller:

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

在登录完成后,即可通过认证得到的access_token调用CallApiUsingUserAccessToken来调用API服务。

总结

本文为IdentityServer4做了基本的介绍。
实际上IdentityServer4还可以非常灵活的与ASP.NET Identity 以及 EF Core等组合使用。
另外基于ASP.NET Core,所以IdentityServer4也支持跨平台。

本文版权归作者和博客园共有,欢迎转载,但未经作者同意必须保留此段声明,且在文章页面明显位置给出原文连接,否则保留追究法律责任的权利。 如果觉得还有帮助的话,可以点一下右下角的【推荐】,希望能够持续的为大家带来好的技术文章!想跟我一起进步么?那就【关注】我吧。

原文出处

打开App,阅读手记
0人推荐
发表评论
随时随地看视频慕课网APP