You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

.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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.13 16:34:57