zoukankan      html  css  js  c++  java
  • 【.NET Core】ASP.NET Core之IdentityServer4(1):快速入门

    本文中的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也支持跨平台。

  • 相关阅读:
    OpenCV -- Mat 转 QImage 函数
    Qt--checkbox
    QT 发布release版本
    JS_0014:JS刷新页面
    JS_0013:JS获取文件后缀名
    JS_0012:JS从一个有规则的字符串中随机选择一个字符再循环生成一个新的无规则的字符串
    JQuery0016:JQuery等待页面全部加载完后执行代码块
    JQuery0015:JQuery查找指定元素并修改其属性
    JS_0011:通过JS给div添加html标签内容
    JS_0010:获取url中指定的参数
  • 原文地址:https://www.cnblogs.com/neverc/p/9004590.html
Copyright © 2011-2022 走看看