本文中的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是最简单的一种授权方式。
步骤:
- 创建IdentityServer
- 定义APIs
- 定义Client
- 创建API
- 定义Authentication
- 使用Client
- 请求Token
- 使用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为非常可信的应用才可能使用这种方式。
步骤:
- 定义RO Client 和 User
- 使用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
步骤:
- 配置IdentityServer
- 定义IdentityResources
- 定义mvc client
- 添加Mvc UI
- 创建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。
步骤:
- 定义Client
- 使用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也支持跨平台。
【.NET Core】ASP.NET Core之IdentityServer4(1):快速入门
原文地址:https://www.cnblogs.com/neverc/p/9004590.html