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
相关产品推荐
相关产品推荐

