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

ASP.NET Core Web API如何用JWT限制Basic角色用户仅访问自身数据

ASP.NET Core Web API 基于JWT的角色+资源权限控制实现

完全可行,这种场景是API权限控制的典型需求,结合JWT的身份声明+自定义授权逻辑就能实现。以下是具体实现步骤:

1. 配置JWT认证服务

在.NET 6+的Program.cs(或.NET 5及以前的Startup.cs)中配置JWT认证,确保Token能携带用户ID和角色信息:

var builder = WebApplication.CreateBuilder(args);

// 添加JWT认证
builder.Services.AddAuthentication(JwtBearerDefaults.AuthenticationScheme)
    .AddJwtBearer(options =>
    {
        options.TokenValidationParameters = new TokenValidationParameters
        {
            ValidateIssuer = true,
            ValidateAudience = true,
            ValidateLifetime = true,
            ValidateIssuerSigningKey = true,
            ValidIssuer = builder.Configuration["Jwt:Issuer"],
            ValidAudience = builder.Configuration["Jwt:Audience"],
            IssuerSigningKey = new SymmetricSecurityKey(Encoding.UTF8.GetBytes(builder.Configuration["Jwt:SecretKey"])),
            // 确保角色声明被正确识别
            RoleClaimType = ClaimTypes.Role
        };
    });

2. 生成包含用户ID和角色的JWT Token

在登录接口中,验证用户身份后,将用户ID(作为NameIdentifier声明)和角色(作为Role声明)加入Token:

[AllowAnonymous]
[HttpPost("login")]
public IActionResult Login([FromBody] LoginModel model)
{
    // 从数据库验证用户身份,获取user实体(包含Id和Role)
    var user = _userService.ValidateUser(model.Username, model.Password);
    if (user == null) return Unauthorized();

    // 创建声明列表
    var claims = new List<Claim>
    {
        new Claim(ClaimTypes.NameIdentifier, user.Id.ToString()),
        new Claim(ClaimTypes.Role, user.Role)
    };

    // 生成Token
    var key = new SymmetricSecurityKey(Encoding.UTF8.GetBytes(_configuration["Jwt:SecretKey"]));
    var creds = new SigningCredentials(key, SecurityAlgorithms.HmacSha256);
    var token = new JwtSecurityToken(
        issuer: _configuration["Jwt:Issuer"],
        audience: _configuration["Jwt:Audience"],
        claims: claims,
        expires: DateTime.Now.AddHours(1),
        signingCredentials: creds);

    return Ok(new { token = new JwtSecurityTokenHandler().WriteToken(token) });
}

3. 实现自定义授权策略

3.1 创建自定义权限要求和处理器

针对Basic角色的资源校验需求,实现IAuthorizationRequirement和AuthorizationHandler:

// 自定义权限要求
public class SameUserRequirement : IAuthorizationRequirement { }

// 权限处理器
public class SameUserHandler : AuthorizationHandler<SameUserRequirement>
{
    protected override Task HandleRequirementAsync(AuthorizationHandlerContext context, SameUserRequirement requirement)
    {
        // 获取当前用户ID
        var currentUserId = context.User.FindFirst(ClaimTypes.NameIdentifier)?.Value;
        if (string.IsNullOrEmpty(currentUserId))
        {
            context.Fail();
            return Task.CompletedTask;
        }

        // 获取路由中的用户ID参数(假设路由为api/User/{id})
        if (!context.Resource is HttpContext httpContext)
        {
            context.Fail();
            return Task.CompletedTask;
        }
        var routeUserId = httpContext.Request.RouteValues["id"]?.ToString();

        // 校验:如果是Admin角色直接放行;如果是Basic角色,需匹配用户ID
        var isAdmin = context.User.IsInRole("Admin");
        var isBasicAndSameUser = context.User.IsInRole("Basic") && currentUserId == routeUserId;

        if (isAdmin || isBasicAndSameUser)
        {
            context.Succeed(requirement);
        }
        else
        {
            context.Fail();
        }

        return Task.CompletedTask;
    }
}

3.2 注册授权策略和处理器

在Program.cs中注册授权服务和自定义处理器,并添加策略:

// 添加授权服务
builder.Services.AddAuthorization(options =>
{
    // Admin专属策略:仅允许Admin角色
    options.AddPolicy("AdminOnly", policy => policy.RequireRole("Admin"));
    // 用户资源专属策略:允许Admin或匹配自身ID的Basic用户
    options.AddPolicy("SameUserOrAdmin", policy =>
        policy.Requirements.Add(new SameUserRequirement()));
});

// 注册自定义权限处理器
builder.Services.AddScoped<IAuthorizationHandler, SameUserHandler>();

4. 在控制器中应用权限策略

在UserController的各个Action上应用对应的授权策略:

[ApiController]
[Route("api/[controller]")]
[Authorize]
public class UserController : ControllerBase
{
    // Admin专属:获取所有用户
    [HttpGet]
    [Authorize(Policy = "AdminOnly")]
    public IActionResult GetAllUsers()
    {
        var users = _userService.GetAllUsers();
        return Ok(users);
    }

    // Admin专属:创建用户
    [HttpPost]
    [Authorize(Policy = "AdminOnly")]
    public IActionResult CreateUser([FromBody] UserModel model)
    {
        _userService.CreateUser(model);
        return CreatedAtAction(nameof(GetUserById), new { id = model.Id }, model);
    }

    // 允许Admin或自身ID匹配的Basic用户:获取单个用户
    [HttpGet("{id}")]
    [Authorize(Policy = "SameUserOrAdmin")]
    public IActionResult GetUserById(int id)
    {
        var user = _userService.GetUserById(id);
        if (user == null) return NotFound();
        return Ok(user);
    }

    // 允许Admin或自身ID匹配的Basic用户:更新用户
    [HttpPut("{id}")]
    [Authorize(Policy = "SameUserOrAdmin")]
    public IActionResult UpdateUser(int id, [FromBody] UserModel model)
    {
        if (id != model.Id) return BadRequest();
        _userService.UpdateUser(model);
        return NoContent();
    }

    // 允许Admin或自身ID匹配的Basic用户:删除用户
    [HttpDelete("{id}")]
    [Authorize(Policy = "SameUserOrAdmin")]
    public IActionResult DeleteUser(int id)
    {
        _userService.DeleteUser(id);
        return NoContent();
    }
}

关键说明

  • 确保Token中正确包含用户ID和角色声明,这是权限校验的基础。
  • 自定义授权处理器通过HttpContext获取路由参数,实现资源级别的权限校验,避免在每个Action中重复写校验逻辑。
  • 策略的设计兼顾了Admin的全权限和Basic用户的自资源权限,符合需求场景。

内容的提问来源于stack exchange,提问作者Chaitanya Bathia

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.24 06:48:09