.NET 6 Web API自定义Bearer Token验证与角色授权问题
解决方案:自定义授权策略实现跨API Token验证与角色校验
核心思路
Attribute是编译期实例化的,无法直接注入依赖服务,所以不能用IAuthorizationFilter特性来实现复杂逻辑。正确做法是利用ASP.NET Core的授权系统扩展点:自定义授权需求(Requirement)+处理程序(Handler),结合自定义特性传递角色参数,这样就能正常注入服务完成Token验证与角色校验。
分步实现
1. 定义带Roles参数的自定义授权特性
用来标记需要授权的接口,同时传递角色参数:
[AttributeUsage(AttributeTargets.Method | AttributeTargets.Class, AllowMultiple = true)] public class MyAuthorizeAttribute : Attribute, IAuthorizationRequirementData { public string Roles { get; set; } public IEnumerable<IAuthorizationRequirement> GetRequirements() { yield return new MyAuthorizationRequirement { Roles = Roles?.Split(',', StringSplitOptions.RemoveEmptyEntries) ?? Array.Empty<string>() }; } }
2. 定义授权需求类
承载角色参数,传递给授权处理程序:
public class MyAuthorizationRequirement : IAuthorizationRequirement { public string[] Roles { get; set; } }
3. 实现授权处理程序(核心逻辑)
注入HttpClient(调用第三方认证API)和数据库上下文,完成Token验证、userId提取、角色校验:
public class MyAuthorizationHandler : AuthorizationHandler<MyAuthorizationRequirement> { private readonly HttpClient _httpClient; private readonly YourDbContext _dbContext; // 构造函数注入所需服务 public MyAuthorizationHandler(HttpClient httpClient, YourDbContext dbContext) { _httpClient = httpClient; _dbContext = dbContext; } protected override async Task HandleRequirementAsync(AuthorizationHandlerContext context, MyAuthorizationRequirement requirement) { // 1. 从请求头获取Bearer Token if (!context.HttpContext.Request.Headers.TryGetValue("Authorization", out var authHeader) || !authHeader.ToString().StartsWith("Bearer ")) { context.Fail(); return; } var token = authHeader.ToString().Replace("Bearer ", string.Empty); try { // 2. 调用第三方认证API验证Token有效性 var validationRequest = new StringContent( JsonSerializer.Serialize(new { Token = token }), Encoding.UTF8, "application/json"); var validationResponse = await _httpClient.PostAsync("https://第三方认证API地址/token/validate", validationRequest); if (!validationResponse.IsSuccessStatusCode) { context.Fail(); return; } // 3. 解析Token获取userId var jwtHandler = new JwtSecurityTokenHandler(); if (!jwtHandler.CanReadToken(token)) { context.Fail(); return; } var jwtToken = jwtHandler.ReadJwtToken(token); var userIdClaim = jwtToken.Claims.FirstOrDefault(c => c.Type == "NameIdentifier"); if (userIdClaim == null || !Guid.TryParse(userIdClaim.Value, out var userId)) { context.Fail(); return; } // 4. 查询数据库验证用户角色 var user = await _dbContext.Users.Include(u => u.Roles) .FirstOrDefaultAsync(u => u.Id == userId); if (user == null) { context.Fail(); return; } // 校验用户是否拥有指定角色 if (requirement.Roles.Any()) { var userRoles = user.Roles.Select(r => r.Name).ToList(); if (!requirement.Roles.Intersect(userRoles).Any()) { context.Fail(); return; } } // 所有校验通过 context.Succeed(requirement); } catch (Exception) { context.Fail(); } } }
4. 配置Program.cs
注册服务、启用认证授权:
var builder = WebApplication.CreateBuilder(args); // 添加控制器 builder.Services.AddControllers(); // 注册HttpClient(调用第三方认证API) builder.Services.AddHttpClient(); // 注册数据库上下文(替换为你的DbContext) builder.Services.AddDbContext<YourDbContext>(options => options.UseSqlServer(builder.Configuration.GetConnectionString("DefaultConnection"))); // 注册授权处理程序 builder.Services.AddScoped<IAuthorizationHandler, MyAuthorizationHandler>(); // 配置授权系统 builder.Services.AddAuthorization(); var app = builder.Build(); // 启用认证和授权(顺序不可颠倒) app.UseAuthentication(); app.UseAuthorization(); app.MapControllers(); app.Run();
5. 在控制器上使用自定义特性
现在可以按预期指定Roles参数:
[ApiController] [Route("api/myapi")] public class MyController : ControllerBase { [HttpGet("test-me")] [MyAuthorize(Roles = "Admin")] public async Task<IActionResult> TestMe() { // 业务逻辑 return Ok(); } [HttpGet("test-user")] [MyAuthorize(Roles = "User,Editor")] public async Task<IActionResult> TestUser() { // 业务逻辑 return Ok(); } }
关键说明
- 放弃
IAuthorizationFilter的原因:Attribute实例编译时创建,无法获取运行时服务容器,无法注入依赖;而授权Handler是依赖注入创建的,可正常获取所需服务。 - 第三方Token验证逻辑可根据实际API要求调整(如请求方式、参数格式)。
- 角色校验逻辑可根据你的数据库结构调整(如用户-角色关联方式)。
内容的提问来源于stack exchange,提问作者Jivopis
相关产品推荐
相关产品推荐

