9.9 IdentityServer (授权服务器) 9.9 IdentityServer (授权服务器) IdentityServer 是一个用于 ASP.NET Core 的开源框架,它实现了 OpenID Connect (OIDC) 和 OAuth 2.0 协议。它允许你集中管理身份验证和授权,并将这些功能从你的应用程序中解耦出来。这使得你的应用程序更加安全、可维护和可扩展。 9.9.1 为什么需要 IdentityServer? 在现代应用程序架构中,特别是微服务架构中,多个应用程序可能需要访问相同的用户身份信息。如果每个应用程序都自己处理身份验证和授权,会导致以下问题: 重复代码: 每个应用程序都需要实现相同的身份验证和授权逻辑。
IdentityServer 是一个用于 ASP.NET Core 的开源框架,它实现了 OpenID Connect (OIDC) 和 OAuth 2.0 协议。它允许你集中管理身份验证和授权,并将这些功能从你的应用程序中解耦出来。这使得你的应用程序更加安全、可维护和可扩展。
在现代应用程序架构中,特别是微服务架构中,多个应用程序可能需要访问相同的用户身份信息。如果每个应用程序都自己处理身份验证和授权,会导致以下问题:
重复代码: 每个应用程序都需要实现相同的身份验证和授权逻辑。
安全漏洞: 每个应用程序都需要维护自己的用户数据库和安全策略,这增加了安全漏洞的风险。
维护困难: 当用户身份信息或安全策略发生变化时,需要更新所有应用程序。
IdentityServer 通过提供一个集中的身份验证和授权服务来解决这些问题。它可以处理用户认证、授权和令牌颁发,并将这些功能从你的应用程序中抽象出来。
在深入研究代码之前,了解 IdentityServer 的核心概念至关重要:
Clients (客户端): 代表想要访问受保护资源的应用程序。客户端可以是 Web 应用程序、移动应用程序、API 等。
Resources (资源): 代表需要保护的 API 或数据。资源需要客户端提供有效的访问令牌才能访问。
Identity Resources (身份资源): 代表关于用户的声明信息,例如姓名、电子邮件地址等。
API Resources (API 资源): 代表 API 本身,用于定义 API 的名称、范围和声明。
Scopes (范围): 代表客户端请求访问的资源的权限。例如,一个客户端可能需要 read 范围来读取数据,而需要 write 范围来写入数据。
Users (用户): 代表使用应用程序的最终用户。
Authorization Server (授权服务器): IdentityServer 本身,负责验证用户身份、授权客户端访问资源和颁发访问令牌。
Access Token (访问令牌): 一个短期令牌,客户端使用它来访问受保护的资源。
Refresh Token (刷新令牌): 一个长期令牌,客户端可以使用它来获取新的访问令牌,而无需用户再次登录。
以下是一个基本的 IdentityServer 配置示例,展示了如何配置客户端、资源和用户。
1. 安装 IdentityServer4 NuGet 包:
Install-Package IdentityServer4 Install-Package IdentityServer4.AspNetIdentity
2. 配置 Startup.cs:
using IdentityServer4.Models; using Microsoft.AspNetCore.Builder; using Microsoft.AspNetCore.Hosting; using Microsoft.Extensions.DependencyInjection; using System.Collections.Generic; using Microsoft.Extensions.Hosting; using Microsoft.AspNetCore.Identity; using Microsoft.EntityFrameworkCore; public class Startup { public void ConfigureServices(IServiceCollection services) { // 配置 Entity Framework Core Identity services.AddDbContext<ApplicationDbContext>(options => options.UseInMemoryDatabase("AspNetIdentity")); // 使用内存数据库,方便演示 services.AddIdentity<IdentityUser, IdentityRole>() .AddEntityFrameworkStores<ApplicationDbContext>() .AddDefaultTokenProviders(); // 配置 IdentityServer services.AddIdentityServer() .AddDeveloperSigningCredential() // 用于开发环境,生产环境请使用证书 .AddInMemoryClients(GetClients()) .AddInMemoryIdentityResources(GetIdentityResources()) .AddInMemoryApiResources(GetApiResources()) .AddAspNetIdentity<IdentityUser>(); // 使用 ASP.NET Identity 管理用户 } public void Configure(IApplicationBuilder app, IWebHostEnvironment env) { if (env.IsDevelopment()) { app.UseDeveloperExceptionPage(); } app.UseRouting(); app.UseIdentityServer(); // 启用 IdentityServer app.UseEndpoints(endpoints => { endpoints.MapDefaultControllerRoute(); }); } // 定义客户端 public static IEnumerable<Client> GetClients() { return new List<Client> { new Client { ClientId = "mvc", ClientName = "MVC Client", AllowedGrantTypes = GrantTypes.Code, RequirePkce = true, RequireClientSecret = false, RedirectUris = { "http://localhost:5002/signin-oidc" }, PostLogoutRedirectUris = { "http://localhost:5002/signout-callback-oidc" }, AllowedScopes = { "openid", "profile", "api1" }, AllowOfflineAccess = true // 允许获取 Refresh Token } }; } // 定义身份资源 public static IEnumerable<IdentityResource> GetIdentityResources() { return new List<IdentityResource> { new IdentityResources.OpenId(), new IdentityResources.Profile() }; } // 定义 API 资源 public static IEnumerable<ApiResource> GetApiResources() { return new List<ApiResource> { new ApiResource("api1", "My API") { Scopes = { "api1" } } }; } } // 简单的 ApplicationDbContext public class ApplicationDbContext : IdentityDbContext { public ApplicationDbContext(DbContextOptions<ApplicationDbContext> options) : base(options) { } }
3. 添加Controller:
using Microsoft.AspNetCore.Mvc; using Microsoft.AspNetCore.Authorization; namespace YourProject.Controllers { public class HomeController : Controller { public IActionResult Index() { return View(); } [Authorize] public IActionResult Privacy() { return View(); } } }
4. 添加View:
创建一个简单的 Index.cshtml 和 Privacy.cshtml 视图。
代码解释:
AddIdentityServer(): 注册 IdentityServer 服务。
AddDeveloperSigningCredential(): 用于开发环境的签名证书。在生产环境中,你需要使用真正的证书。
AddInMemoryClients(): 使用内存数据库存储客户端配置。在生产环境中,你需要使用持久化存储,例如数据库。
AddInMemoryIdentityResources(): 使用内存数据库存储身份资源配置。
AddInMemoryApiResources(): 使用内存数据库存储 API 资源配置。
AddAspNetIdentity<IdentityUser>(): 集成 ASP.NET Identity,使用 ASP.NET Identity 管理用户。
GetClients(): 定义客户端配置。 ClientId 是客户端的唯一标识符。 AllowedGrantTypes 定义了客户端可以使用的授权类型。 RedirectUris 定义了授权服务器重定向回客户端的 URL。 AllowedScopes 定义了客户端可以请求访问的范围。
GetIdentityResources(): 定义身份资源配置。 openid 范围用于请求用户的身份信息。 profile 范围用于请求用户的个人资料信息。
GetApiResources(): 定义 API 资源配置。 api1 是 API 的名称。
IdentityServer 提供了多种扩展点,允许你自定义其行为以满足你的特定需求。
自定义用户存储: 你可以使用自己的用户存储,例如关系数据库、NoSQL 数据库或外部身份提供商。
自定义授权逻辑: 你可以自定义授权逻辑,例如基于角色的访问控制 (RBAC) 或基于属性的访问控制 (ABAC)。
自定义令牌颁发: 你可以自定义令牌颁发过程,例如添加自定义声明或使用不同的令牌格式。
自定义 UI: 你可以自定义 IdentityServer 的 UI,例如登录页面、授权页面和错误页面。
要使用自定义用户存储,你需要实现 IProfileService 和 IResourceOwnerPasswordValidator 接口。
IProfileService 接口用于获取用户的声明信息。
IResourceOwnerPasswordValidator 接口用于验证用户的用户名和密码。
以下是一个使用自定义用户存储的示例:
using IdentityServer4.Models; using IdentityServer4.Services; using System.Collections.Generic; using System.Security.Claims; using System.Threading.Tasks; using IdentityServer4.Validation; public class CustomProfileService : IProfileService { // 自定义用户存储 private readonly IUserRepository _userRepository; public CustomProfileService(IUserRepository userRepository) { _userRepository = userRepository; } public async Task GetProfileDataAsync(ProfileDataRequestContext context) { // 根据用户 ID 获取用户 var user = await _userRepository.GetUserById(context.Subject.GetSubjectId()); if (user != null) { // 添加声明 var claims = new List<Claim> { new Claim("given_name", user.FirstName), new Claim("family_name", user.LastName) }; context.IssuedClaims = claims; } } public async Task IsActiveAsync(IsActiveContext context) { // 检查用户是否处于活动状态 var user = await _userRepository.GetUserById(context.Subject.GetSubjectId()); context.IsActive = user != null && user.IsActive; } } public class CustomResourceOwnerPasswordValidator : IResourceOwnerPasswordValidator { private readonly IUserRepository _userRepository; public CustomResourceOwnerPasswordValidator(IUserRepository userRepository) { _userRepository = userRepository; } public async Task ValidateAsync(ResourceOwnerPasswordValidationContext context) { // 验证用户名和密码 var user = await _userRepository.ValidateUserCredentials(context.UserName, context.Password); if (user != null) { context.Result = new GrantValidationResult(user.Id.ToString(), "custom"); } else { context.Result = new GrantValidationResult(TokenRequestErrors.InvalidGrant, "invalid custom credential"); } } }
然后,你需要将自定义服务注册到 IdentityServer 中:
services.AddIdentityServer() .AddDeveloperSigningCredential() .AddInMemoryClients(GetClients()) .AddInMemoryIdentityResources(GetIdentityResources()) .AddInMemoryApiResources(GetApiResources()) .AddProfileService<CustomProfileService>() // 注册自定义 ProfileService .AddResourceOwnerValidator<CustomResourceOwnerPasswordValidator>(); // 注册自定义 ResourceOwnerPasswordValidator
你可以使用 IAuthorizeInteractionResponseGenerator 接口自定义授权逻辑。 此接口允许你控制用户是否应该被提示授予客户端访问其数据的权限。
你可以使用 ITokenCustomizer 接口自定义令牌颁发过程。 此接口允许你在令牌中添加自定义声明,或者使用不同的令牌格式。
你可以通过替换 IdentityServer 的默认视图来自定义 UI。 你需要创建自己的视图文件,并将它们放置在 Views/Account 目录中。 你还可以使用自定义 CSS 样式和 JavaScript 脚本来更改 UI 的外观和感觉。
IdentityServer 可以部署到各种环境中,例如:
本地开发环境: 你可以使用 Visual Studio 或 .NET CLI 在本地开发环境中运行 IdentityServer。
云环境: 你可以将 IdentityServer 部署到云环境,例如 Azure、AWS 或 Google Cloud。
容器环境: 你可以将 IdentityServer 部署到容器环境,例如 Docker 或 Kubernetes。
使用 HTTPS: 始终使用 HTTPS 来保护 IdentityServer 和你的应用程序之间的通信。
保护你的证书: 保护用于签名令牌的证书。不要将证书存储在源代码管理系统中。
定期更新 IdentityServer: 定期更新 IdentityServer 到最新版本,以修复安全漏洞。
配置适当的 CORS 策略: 配置适当的 CORS 策略,以防止跨域请求伪造 (CSRF) 攻击。
使用强密码: 使用强密码来保护用户帐户。
启用多因素身份验证 (MFA): 启用 MFA 以增加用户帐户的安全性。
IdentityServer 是一个功能强大的框架,用于构建安全的身份验证和授权系统。 它提供了多种扩展点,允许你自定义其行为以满足你的特定需求。 通过遵循安全最佳实践,你可以使用 IdentityServer 来保护你的应用程序和用户数据。 本章节只是一个入门,IdentityServer 的功能远不止这些,建议深入研究官方文档。