ASP.NET Core 8中同时支持Cookie、Bearer Token认证及角色权限管理的配置问题
我来帮你搞定这个同时支持双认证+角色管理的问题,咱们一步步梳理调整:
一、核心问题分析
你之前的几次配置踩了两个关键坑:
- 用
AddDefaultIdentity时没启用角色支持,导致RoleManager无法注册 - 切换到
AddIdentity<IdentityUser, IdentityRole>后,没正确配置Bearer认证的关联逻辑,导致Token登录报错 - 用
AddIdentityApiEndpoints时,默认生成的Token没包含角色Claims,所以角色策略授权返回403
二、正确的服务注册配置
直接上可运行的完整配置,我会逐段解释:
// 1. 先注册数据库上下文 builder.Services.AddDbContext<ApplicationDbContext>(options => options.UseSqlServer(builder.Configuration.GetConnectionString("Communities"))); // 2. 注册Identity并启用角色支持 builder.Services.AddIdentity<IdentityUser, IdentityRole>(options => { // 这里可以按需配置密码、锁定等Identity规则,比如: // options.Password.RequireDigit = true; }) .AddEntityFrameworkStores<ApplicationDbContext>() .AddDefaultTokenProviders() // 用于生成密码重置、邮箱验证等Token .AddApiEndpoints(); // 启用Identity自带的API端点(登录、注册等) // 3. 配置双认证方案:Cookie + Bearer Token builder.Services.AddAuthentication(options => { // 设置默认认证/挑战方案,这里用Identity默认的Cookie方案,也可以改成Bearer options.DefaultAuthenticateScheme = IdentityConstants.ApplicationScheme; options.DefaultChallengeScheme = IdentityConstants.ApplicationScheme; options.DefaultSignInScheme = IdentityConstants.ApplicationScheme; }) .AddCookie(IdentityConstants.ApplicationScheme) // 保留Identity默认的Cookie认证 .AddJwtBearer(options => { // 配置JWT验证参数,要和你生成Token时的参数一致 options.TokenValidationParameters = new TokenValidationParameters { ValidateIssuer = true, ValidateAudience = true, ValidateLifetime = true, ValidateIssuerSigningKey = true, ValidIssuer = builder.Configuration["Jwt:Issuer"], // 从配置文件读,比如appsettings.json ValidAudience = builder.Configuration["Jwt:Audience"], IssuerSigningKey = new SymmetricSecurityKey(Encoding.UTF8.GetBytes(builder.Configuration["Jwt:Key"])) }; // 可选:支持从QueryString获取Token(方便调试) options.Events = new JwtBearerEvents { OnMessageReceived = context => { var token = context.Request.Query["access_token"]; if (!string.IsNullOrEmpty(token)) { context.Token = token; } return Task.CompletedTask; } }; }); // 4. 配置授权策略 builder.Services.AddAuthorizationBuilder() .AddPolicy(Policy.Admin, policy => policy .RequireAuthenticatedUser() .RequireRole(Roles.Admin)); // 5. 注册自定义邮件发送器 builder.Services.AddTransient<IEmailSender<IdentityUser>, CommunitiesEmailSender<IdentityUser>>(); // 关键:添加自定义Claims工厂,确保Token中包含角色信息 builder.Services.AddScoped<IUserClaimsPrincipalFactory<IdentityUser>, CustomUserClaimsPrincipalFactory>();
补充:自定义Claims工厂代码
这个类的作用是把用户的角色信息添加到Token的Claims中,否则授权策略无法识别用户角色:
public class CustomUserClaimsPrincipalFactory : UserClaimsPrincipalFactory<IdentityUser, IdentityRole> { public CustomUserClaimsPrincipalFactory(UserManager<IdentityUser> userManager, RoleManager<IdentityRole> roleManager, IOptions<IdentityOptions> optionsAccessor) : base(userManager, roleManager, optionsAccessor) { } protected override async Task<ClaimsIdentity> GenerateClaimsAsync(IdentityUser user) { var identity = await base.GenerateClaimsAsync(user); // 把用户的所有角色添加到Claims里 var roles = await UserManager.GetRolesAsync(user); foreach (var role in roles) { identity.AddClaim(new Claim(ClaimTypes.Role, role)); } return identity; } }
三、中间件顺序必须正确
中间件的执行顺序直接影响认证授权是否生效,一定要按这个顺序写:
var app = builder.Build(); // 先映射Identity的API端点(登录、注册等) app.MapIdentityApi<IdentityUser>(); // 认证必须在授权之前! app.UseAuthentication(); app.UseAuthorization(); // 最后映射你的控制器路由 app.MapControllers(); app.Run();
四、控制器中使用RoleManager
现在配置完成后,你可以直接在控制器中注入RoleManager<IdentityRole>和UserManager<IdentityUser>了:
private readonly RoleManager<IdentityRole> _roleManager; private readonly UserManager<IdentityUser> _userManager; public YourController(RoleManager<IdentityRole> roleManager, UserManager<IdentityUser> userManager) { _roleManager = roleManager; _userManager = userManager; } [HttpPost] [Authorize(Policy = Policy.Admin)] [Route(nameof(SetUserRole))] public async Task<IActionResult> SetUserRole(string userName, string desiredRole) { // 检查目标角色是否存在 var roleExists = await _roleManager.RoleExistsAsync(desiredRole); if (!roleExists) { return BadRequest($"角色「{desiredRole}」不存在"); } // 查找目标用户 var user = await _userManager.FindByNameAsync(userName); if (user == null) { return NotFound($"用户「{userName}」不存在"); } // 添加角色 var result = await _userManager.AddToRoleAsync(user, desiredRole); if (result.Succeeded) { return Ok("角色添加成功"); } return BadRequest(result.Errors.Select(e => e.Description)); }
五、验证要点
- 确保你的
appsettings.json中配置了JWT相关参数:
"Jwt": { "Issuer": "YourAppIssuer", "Audience": "YourAppAudience", "Key": "YourSuperSecretKeyAtLeast16CharsLong" }
- 登录获取Token后,用Postman等工具测试时,把Token放在
Authorization头中,格式为Bearer {你的Token} - 给用户分配Admin角色后,重新登录获取Token,此时Token中会包含角色Claims,就能通过
Admin策略的授权了
备注:内容来源于stack exchange,提问作者Juan Pablo Gomez
相关产品推荐
相关产品推荐

