如何为AspNet Core 3.0-preview React IdentityServer4项目添加授权?
我刚好处理过类似的场景,针对你用dotnet new react --name myproject --auth Individual创建的带IdentityServer4的ASP.NET Core 3.0 Preview7项目,要实现[Authorize(Roles = "Administrator")]这类授权特性,得从IdentityServer资源配置、ASP.NET Core服务配置、角色初始化这几个关键环节入手,下面是详细的操作流程:
1. 配置IdentityServer的身份与API资源
IdentityServer4需要明确将角色声明纳入返回的Token中,否则API无法识别用户的角色信息。找到项目中的Config.cs文件(若模板默认未生成,可手动创建),修改以下方法:
1.1 添加角色身份资源
让IdentityServer支持角色声明的身份资源:
public static IEnumerable<IdentityResource> GetIdentityResources() { return new List<IdentityResource> { new IdentityResources.OpenId(), new IdentityResources.Profile(), // 添加角色身份资源,指定声明类型为"role" new IdentityResource("roles", "用户角色", new List<string> { "role" }) }; }
1.2 配置API资源接受角色声明
确保你的API资源允许接收角色声明:
public static IEnumerable<ApiResource> GetApiResources() { return new List<ApiResource> { new ApiResource("myprojectAPI", "My Project API") { // 让API识别并接收role类型的声明 UserClaims = { JwtClaimTypes.Role } } }; }
1.3 更新客户端请求范围
修改客户端配置,让客户端在请求Token时包含roles范围:
public static IEnumerable<Client> GetClients() { return new List<Client> { new Client { ClientId = "myproject", ClientName = "myproject", AllowedGrantTypes = GrantTypes.Code, RequirePkce = true, RequireClientSecret = false, RedirectUris = { "https://localhost:5001/authentication/login-callback" }, PostLogoutRedirectUris = { "https://localhost:5001/" }, AllowedCorsOrigins = { "https://localhost:5001" }, AllowedScopes = { IdentityServerConstants.StandardScopes.OpenId, IdentityServerConstants.StandardScopes.Profile, "myprojectAPI", // 新增roles范围 "roles" }, AllowOfflineAccess = true } }; }
2. 配置Startup.cs中的服务
调整Identity和授权服务的配置,确保角色支持生效:
2.1 启用Identity角色支持
替换模板默认的AddDefaultIdentity,改为完整的Identity配置以支持角色:
services.AddDbContext<ApplicationDbContext>(options => options.UseSqlServer( Configuration.GetConnectionString("DefaultConnection"))); // 启用Identity用户与角色的EF存储支持 services.AddIdentity<IdentityUser, IdentityRole>() .AddEntityFrameworkStores<ApplicationDbContext>() .AddDefaultTokenProviders() .AddDefaultUI();
2.2 配置IdentityServer集成
确保IdentityServer使用我们配置的资源,并关联Identity角色:
services.AddIdentityServer() .AddApiAuthorization<IdentityUser, ApplicationDbContext>(options => { // 确保API资源包含role声明(若未使用Config.cs则需配置此项) options.ApiResources.Single().UserClaims.Add("role"); }) .AddIdentityResources() .AddClients() .AddAspNetIdentity<IdentityUser>();
2.3 配置授权策略
添加授权策略支持,既可以使用默认的角色授权,也可以自定义策略:
services.AddAuthorization(options => { // 自定义管理员策略(可选) options.AddPolicy("AdminOnly", policy => policy.RequireRole("Administrator")); // 默认的角色授权已自动支持,直接用[Authorize(Roles="Administrator")]即可 });
2.4 映射JWT角色声明类型
ASP.NET Core默认的角色声明类型与IdentityServer的默认值不同,需要手动映射确保识别:
services.AddAuthentication() .AddJwtBearer(options => { options.TokenValidationParameters = new TokenValidationParameters { // 将JWT中的"role"声明映射为ASP.NET Core的角色声明 RoleClaimType = JwtClaimTypes.Role }; });
3. 初始化角色与测试用户
在项目启动时自动创建Administrator角色并分配给测试用户,方便验证:
在Program.cs的CreateHostBuilder方法末尾添加初始化逻辑:
var host = CreateHostBuilder(args).Build(); using (var scope = host.Services.CreateScope()) { var services = scope.ServiceProvider; try { var roleManager = services.GetRequiredService<RoleManager<IdentityRole>>(); var userManager = services.GetRequiredService<UserManager<IdentityUser>>(); // 创建Administrator角色 if (!await roleManager.RoleExistsAsync("Administrator")) { var adminRole = new IdentityRole("Administrator"); await roleManager.CreateAsync(adminRole); } // 创建测试管理员用户(可选) var adminUser = await userManager.FindByEmailAsync("admin@example.com"); if (adminUser == null) { adminUser = new IdentityUser { UserName = "admin@example.com", Email = "admin@example.com" }; await userManager.CreateAsync(adminUser, "Password123!"); await userManager.AddToRoleAsync(adminUser, "Administrator"); } } catch (Exception ex) { var logger = services.GetRequiredService<ILogger<Program>>(); logger.LogError(ex, "初始化角色与用户时发生错误"); } } host.Run();
4. 在控制器中使用授权特性
现在就可以在控制器方法上直接使用授权特性了:
[ApiController] [Route("[controller]")] public class WeatherForecastController : ControllerBase { [HttpGet] [Authorize(Roles = "Administrator")] public IEnumerable<WeatherForecast> Get() { // 仅管理员可访问的逻辑 var rng = new Random(); return Enumerable.Range(1, 5).Select(index => new WeatherForecast { Date = DateTime.Now.AddDays(index), TemperatureC = rng.Next(-20, 55), Summary = Summaries[rng.Next(Summaries.Length)] }) .ToArray(); } }
内容的提问来源于stack exchange,提问作者Andreas Sjöberg

