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

本文中的IdentityServer4基於上節的jenkins 進行docker自動化部署。
使用了MariaDB,EF Core,AspNetIdentity,Dockerhtml

Demo地址:https://sso.neverc.cn
Demo源碼:https://github.com/NeverCL/Geek.IdentityServer4git

簡介

OpenID Connect :經常使用的認證協議有SAML2p, WS-Federation and OpenID Connect – SAML2p。OpenID Connect是其中最新的協議。github

OAuth 2.0 :OAuth 2.0 是一種受權協議。經過Access Token能夠訪問受保護的API接口。web

OpenID Connect和OAuth 2.0很是類似,實際上,OpenID Connect是OAuth 2.0之上的一個擴展。
身份認證和API訪問這兩個基本的安全問題被合併爲一個協議 - 每每只需一次往返安全令牌服務。docker

IdentityServer4基於ASP.NET Core 2對這兩種協議的實現。shell

支持規範:https://identityserver4.readthedocs.io/en/release/intro/specs.htmljson

關鍵詞

IdentityServer:提供OpenID Connect and OAuth 2.0 protocols.c#

User:IdentityServer中的用戶api

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也支持跨平臺。

相關文章
相關標籤/搜索