本文共 7531 字,大约阅读时间需要 25 分钟。
身份认证服务器是一个向客户端发送安全令牌的软件。它的主要功能包括:
身份认证服务器主要支持以下协议:
用户是使用客户端的人,通过身份认证服务器获取访问令牌,从而能够访问受保护的API。
客户端是请求身份认证服务器获取令牌的软件,用于认证用户(获取身份令牌)或访问资源(获取访问令牌)。客户端必须先注册于身份认证服务器,才能请求令牌。常见类型包括:
资源是被保护的API或身份数据。每个API有一个唯一的名称,客户端使用该名称指定要访问的API。
身份令牌表示身份认证完成的结果,至少包含用户的身份信息及权限信息。它通常用于验证用户身份。
访问令牌用于获取API访问权限。客户端通过提交访问令牌向受保护API进行认证,令牌包含客户端和使用者的信息。
在没有用户参与的程序中,客户端使用客户端凭据(Client Credentials)进行认证。这种方式不涉及用户交互,完全依赖于程序的配置。
配置IdentityServer4的步骤如下:
Config类中定义API和客户端。public static class Config{ public static IEnumerable ApiScopes { return new List { new ApiScope("api1", "My API") }; } public static IEnumerable Clients { return new List { new Client { ClientId = "client", AllowedGrantTypes = GrantTypes.ClientCredentials, ClientSecrets = { new Secret("secret".Sha256()) }, AllowedScopes = { "api1" } } }; }} Startup类中添加IdentityServer4服务,并配置客户端。public class Startup : IStartup{ public IConfiguration Configuration { get; set; } public Startup(IConfiguration configuration) { Configuration = configuration; } public void ConfigureServices(IServiceCollection services) { services.AddControllers(); var builder = services.AddIdentityServer() .AddInMemoryApiScopes(Config.ApiScopes) .AddInMemoryClients(Config.Clients) .AddDeveloperSigningCredential(); } public void Configure(IApplicationBuilder app, IWebHostEnvironment env) { if (env.IsDevelopment()) { app.UseDeveloperExceptionPage(); } app.UseRouting(); app.UseAuthentication(); app.UseAuthorization(); app.UseEndpoints(endpoints => { endpoints.MapControllers(); }); }} dotnet run --urls http://*:5001
IdentityController中使用[Authorize]属性标记受保护的端点。[ApiController]public class IdentityController : ControllerBase{ [Authorize] [HttpGet("identity")] public IActionResult Get() { return new JsonResult(from c in User.Claims select new { c.Type, c.Value }); } [Authorize] [HttpGet("test")] public string Getst() { return "受保护的API访问成功"; }} Startup类中添加认证和授权服务。public void ConfigureServices(IServiceCollection services){ services.AddControllers(); services.AddAuthentication("Bearer") .AddJwtBearer("Bearer", options => { options.Authority = "https://localhost:5001"; options.TokenValidationParameters = new TokenValidationParameters { ValidateAudience = false }; }); services.AddAuthorization(options => { options.AddPolicy("ApiScope", policy => { policy.RequireAuthenticatedUser(); policy.RequireClaim("scope", "api1"); }); });}public void Configure(IApplicationBuilder app, IWebHostEnvironment env){ if (env.IsDevelopment()) { app.UseDeveloperExceptionPage(); } app.UseRouting(); app.UseAuthentication(); app.UseAuthorization(); app.UseEndpoints(endpoints => { endpoints.MapControllers(); });} public class Program{ public static async Task Main(string[] args) { var client = new HttpClient(); var disco = await client.GetDiscoveryDocumentAsync("http://localhost:5001"); if (disco.IsError) { Console.WriteLine($"连接失败:{disco.Error}"); return; } var tokenResponse = await client.RequestClientCredentialsTokenAsync(new ClientCredentialsTokenRequest { Address = disco.TokenEndpoint, ClientId = "client", ClientSecret = "secret", Scope = "api1" }); if (tokenResponse.IsError) { Console.WriteLine($"token返回错误:{tokenResponse.Error}"); return; } Console.WriteLine(tokenResponse.Json); var apiClient = new HttpClient(); apiClient.SetBearerToken(tokenResponse.AccessToken); var response = await apiClient.GetAsync("http://localhost:6001/test"); if (!response.IsSuccessStatusCode) { Console.WriteLine(response.StatusCode); Console.WriteLine("访问失败"); } else { var content = await response.Content.ReadAsStringAsync(); Console.WriteLine("访问成功"); Console.WriteLine(content); } Console.ReadLine(); }} WPF客户端涉及用户交互,使用密码grant类型进行认证。
private async TaskRenewTokensAsync(){ var client = new HttpClient(); var disco = await client.GetDiscoveryDocumentAsync("http://localhost:5000"); if (disco.IsError) { throw new Exception(disco.Error); } var refreshToken = await HttpContext.GetTokenAsync(OpenIdConnectParameterNames.RefreshToken); var tokenResponse = await client.RequestRefreshTokenAsync(new RefreshTokenRequest { Address = disco.TokenEndpoint, ClientId = "mvc client", ClientSecret = "mvc secret", Scope = "api1 openid profile email phone address", GrantType = OpenIdConnectGrantTypes.RefreshToken, RefreshToken = refreshToken }); if (tokenResponse.IsError) { throw new Exception(tokenResponse.Error); } var expiresAt = DateTime.UtcNow + TimeSpan.FromSeconds(tokenResponse.ExpiresIn); var tokens = new[] { new AuthenticationToken { Name = OpenIdConnectParameterNames.IdToken, Value = tokenResponse.IdentityToken }, new AuthenticationToken { Name = OpenIdConnectParameterNames.AccessToken, Value = tokenResponse.AccessToken }, new AuthenticationToken { Name = OpenIdConnectParameterNames.RefreshToken, Value = tokenResponse.RefreshToken }, new AuthenticationToken { Name = "expires_at", Value = expiresAt.ToString("o", CultureInfo.InvariantCulture) } }; await HttpContext.AuthenticateAsync(CookieAuthenticationDefaults.AuthenticationScheme); currentAuthenticateResult.Properties.StoreTokens(tokens); await HttpContext.SignInAsync(CookieAuthenticationDefaults.AuthenticationScheme, currentAuthenticateResult.Principal, currentAuthenticateResult.Properties); return tokenResponse.AccessToken;}
混合流结合了隐式流和客户端凭据流的优点,适用于需要获取身份令牌和访问令牌的场景。
response_type = code Id_tokenresponse_type = code tokenresponse_type = code id_token token[Authorize(Roles = "管理员,普通用户")]限制访问权限。RequireClaim(JwtClaimTypes.FamilyName, "Smith")。混合流允许客户端在不直接暴露访问令牌的情况下,通过后端通道获取令牌,从而保护敏感信息。
[1] [2] [3] [4]
转载地址:http://xuwl.baihongyu.com/