文章目录
前言
在ASP.NET Core Web API 中实现基于JWT 的RBAC(基于角色的访问控制)。
一、JWT与RBAC
JWT (JSON Web Token)与 RBAC (基于角色的访问控制,Role-Based Access Access Control)结合使用时,是一种通过令牌(Token)传递用户角色信息,并基于角色实现权限管理的安全机制。
二、JWT 的作用
- JWT 是一种紧凑的、自包含的令牌格式,通常用于身份验证和授权。其结构分为三部分:
- Header :算法和令牌类型(如 HS256 或 RSA)。
- Payload:存放用户信息(如用户ID、角色)和声明(如过期时间)。
- Signature:对前两部分的签名,确保令牌未被篡改。
三、RBAC 的核心思想
- RBAC 通过角色管理权限,而非直接赋予用户权限:
- 角色(Role) :定义一组权限(如 admin 、user)。
- 权限(Permission) :资源或操作(如 read:data、delete:user)。
- 用户被分配角色,间接获得权限。
四、使用
1、配置文件 (appsettings.json)
-
appsettings.json
csharp{ "JwtSettings": { "Issuer": "yourdomain.com", "Audience": "yourapp", "SecretKey": "YourSuperSecretKeyAtLeast32CharactersLong", "ExpirationMinutes": 60, "RefreshTokenExpirationDays": 7 } "AllowedHosts": "*" }
2、JWT配置模型 (Entity/JwtSettings.cs)
-
JwtSettings.cs
csharpnamespace JWTWebAPI.Entity { public class JwtSettings { public string Issuer { get; set; } = string.Empty; public string Audience { get; set; } = string.Empty; public string SecretKey { get; set; } = string.Empty; public int ExpirationMinutes { get; set; } public int RefreshTokenExpirationDays { get; set; } } }
3、服务扩展类,JWT配置 (Extensions/ServiceExtensions.cs)
-
ServiceExtensions.cs
csharpusing JWTWebAPI.Entity; using Microsoft.AspNetCore.Authentication.JwtBearer; using Microsoft.IdentityModel.Tokens; using System.Security.Claims; using System.Text; namespace JWTWebAPI.Extensions { public static class ServiceExtensions { // JWT认证配置 public static void ConfigureJwtAuthentication(this IServiceCollection services, IConfiguration configuration) { var jwtSettings = configuration.GetSection("JwtSettings").Get<JwtSettings>(); services.AddAuthentication(options => { options.DefaultAuthenticateScheme = JwtBearerDefaults.AuthenticationScheme; options.DefaultChallengeScheme = JwtBearerDefaults.AuthenticationScheme; }) .AddJwtBearer(options => { options.TokenValidationParameters = new TokenValidationParameters { ValidateIssuer = false, ValidIssuer = jwtSettings.Issuer, ValidateAudience = false, ValidAudience = jwtSettings.Audience, ValidateLifetime = false, ValidateIssuerSigningKey = false, IssuerSigningKey = new SymmetricSecurityKey( Encoding.UTF8.GetBytes(jwtSettings.SecretKey!)), //ClockSkew = TimeSpan.Zero, RoleClaimType=ClaimTypes.Role }; options.Events = new JwtBearerEvents { OnAuthenticationFailed = context => { if (context.Exception.GetType() == typeof(SecurityTokenExpiredException)) { context.Response.Headers.Append("Token-Expired", "true"); } return Task.CompletedTask; } }; }); } // 授权策略配置 public static void ConfigureAuthorizationPolicies(this IServiceCollection services) { services.AddAuthorization(options => { // 基于角色的策略 options.AddPolicy("AdminOnly", policy => policy.RequireRole("admin")); options.AddPolicy("ManagerOnly", policy => policy.RequireRole("admin")); // 基于自定义权限的策略 options.AddPolicy("ContentEditor", policy => policy.RequireClaim("permission", "content.edit")); options.AddPolicy("UserManagement", policy => policy.RequireClaim("permission", "user.manage")); }); } } }
4、用户仓库接口服务
-
Interfaces/IUserRepository.cs
csharpusing JWTWebAPI.Entity; namespace JWTWebAPI.Interface { public interface IUserRepository { Task<AspNetUsers?> GetUserByCredentials(string username, string password); Task SaveRefreshToken(long userId, string refreshToken, DateTime expiry); Task<AspNetUsers?> GetUserByRefreshToken(string refreshToken); } }
-
Repositories/UserRepository.cs
csharpusing JWTWebAPI.Data; using JWTWebAPI.Entity; using JWTWebAPI.Interface; using Microsoft.AspNetCore.Identity; using Microsoft.EntityFrameworkCore; using Org.BouncyCastle.Crypto.Generators; namespace JWTWebAPI.Repository { public class UserRepositor : IUserRepository { private readonly MyDbContext _context; private readonly UserManager<AspNetUsers> _userManager; public UserRepositor(MyDbContext context, UserManager<AspNetUsers> userManager, RoleManager<Role> roleManager) { _context = context; _userManager = userManager; } public async Task<AspNetUsers?> GetUserByCredentials(string username, string password) { try { var user=await _userManager.FindByNameAsync(username); if (user == null) return null; return user; } catch (Exception) { throw; } } public async Task<AspNetUsers?> GetUserByRefreshToken(string refreshToken) { return await _context.Users .FirstOrDefaultAsync(u => u.RefreshToken == refreshToken && u.RefreshTokenExpiry > DateTime.UtcNow); } public async Task SaveRefreshToken(long userId, string refreshToken, DateTime expiry) { var user = await _context.Users.FindAsync(userId); if (user != null) { user.RefreshToken = refreshToken; user.RefreshTokenExpiry = expiry; await _context.SaveChangesAsync(); } } } }
5、认证服务 (Interface/IAuthService.cs、Repository/AuthService.cs)
-
IAuthService.cs
csharpusing JWTWebAPI.Entity; namespace JWTWebAPI.Interface { public interface IAuthService { Task<AuthResult> Authenticate(string username, string password); Task<AuthResult> RefreshToken(string token, string refreshToken); } }
-
AuthService.cs
csharpusing JWTWebAPI.Entity; using JWTWebAPI.Interface; using Microsoft.Extensions.Options; using Microsoft.IdentityModel.Tokens; using System.IdentityModel.Tokens.Jwt; using System.Security.Claims; using System.Security.Cryptography; using System.Text; namespace JWTWebAPI.Repository { public class AuthService : IAuthService { private readonly JwtSettings _jwtSettings; private readonly IUserRepository _userRepository; public AuthService(IOptions<JwtSettings> jwtSettings, IUserRepository userRepository) { _jwtSettings = jwtSettings.Value; _userRepository = userRepository; } public async Task<AuthResult> Authenticate(string username, string password) { var user = await _userRepository.GetUserByCredentials(username, password); if (user == null) return null; var claims = new[] { new Claim(ClaimTypes.NameIdentifier, user.Id.ToString()), new Claim(ClaimTypes.Name, user.UserName), new Claim(ClaimTypes.Role, user.Role) // 用户角色 }; var token = GenerateJwtToken(claims); var refreshToken = GenerateRefreshToken(); await _userRepository.SaveRefreshToken(user.Id, refreshToken, DateTime.UtcNow.AddDays(_jwtSettings.RefreshTokenExpirationDays)); return new AuthResult { Token = token, RefreshToken = refreshToken, ExpiresIn = _jwtSettings.ExpirationMinutes * 60 }; } public Task<AuthResult> RefreshToken(string token, string refreshToken) { throw new NotImplementedException(); } private string GenerateJwtToken(IEnumerable<Claim> claims) { var key = new SymmetricSecurityKey(Encoding.UTF8.GetBytes(_jwtSettings.SecretKey)); var creds = new SigningCredentials(key, SecurityAlgorithms.HmacSha256); var token = new JwtSecurityToken( issuer: _jwtSettings.Issuer, audience: _jwtSettings.Audience, claims: claims, expires: DateTime.UtcNow.AddMinutes(_jwtSettings.ExpirationMinutes), signingCredentials: creds ); return new JwtSecurityTokenHandler().WriteToken(token); } private static string GenerateRefreshToken() { var randomNumber = new byte[32]; using var rng = RandomNumberGenerator.Create(); rng.GetBytes(randomNumber); return Convert.ToBase64String(randomNumber); } } }
-
AuthResult.cs
csharpnamespace JWTWebAPI.Entity { public class AuthResult { public string Token { get; set; } public string RefreshToken { get; set; } public int ExpiresIn { get; set; } } }
6、控制器(验证登录权限,生成Token)
-
AuthController.cs
csharpusing JWTWebAPI.Entity; using JWTWebAPI.Interface; using Microsoft.AspNetCore.Authorization; using Microsoft.AspNetCore.Http; using Microsoft.AspNetCore.Identity.Data; using Microsoft.AspNetCore.Mvc; using Microsoft.Extensions.Options; using Microsoft.IdentityModel.Tokens; using System.IdentityModel.Tokens.Jwt; using System.Runtime; using System.Security.Claims; using System.Text; namespace JWTWebAPI.Controllers { [Route("api/[controller]/[action]")] [ApiController] public class AuthController : ControllerBase { private readonly IConfiguration _config; private readonly IOptionsSnapshot<JwtSettings> _settings; private readonly IAuthService _authService; public AuthController(IConfiguration config, IOptionsSnapshot<JwtSettings> settings, IAuthService authService) { _config = config; _settings = settings; _authService = authService; } [HttpPost] [AllowAnonymous] public async Task<IActionResult> Login([FromBody] LoginModel request) { var result = await _authService.Authenticate(request.Username, request.Password); if (result == null) return Unauthorized(); return Ok(result); } [HttpPost] [AllowAnonymous] public async Task<IActionResult> Refresh([FromBody] RefreshTokenRequest request) { var result = await _authService.RefreshToken(request.Token, request.RefreshToken); if (result == null) return Unauthorized(); return Ok(result); } } }
-
LoginModel.cs
csharpnamespace JWTWebAPI.Entity { public class LoginModel { public string Username { get; set; } = string.Empty; public string Password { get; set; } = string.Empty; } }
-
RefreshTokenRequest.cs
csharppublic class RefreshTokenRequest { [Required] public string Token { get; set; } [Required] public string RefreshToken { get; set; } }
7、注册服务
-
Program.cs
csharpusing JWTWebAPI.Data; using JWTWebAPI.Entity; using JWTWebAPI.Extensions; using JWTWebAPI.Interface; using JWTWebAPI.Repository; using Microsoft.AspNetCore.Identity; using Microsoft.EntityFrameworkCore; var builder = WebApplication.CreateBuilder(args); // 添加数据库上下文 var connectionString = builder.Configuration.GetConnectionString("DefaultConnection"); builder.Services.AddDbContext<MyDbContext>(opt => { opt.UseSqlServer(connectionString); }); builder.Services.AddDataProtection(); builder.Services.AddIdentityCore<AspNetUsers>(opt => { opt.Lockout.MaxFailedAccessAttempts = 5;//登录失败多少次账号被锁定 opt.Lockout.DefaultLockoutTimeSpan = TimeSpan.FromMinutes(1);//锁定多长时间 opt.Password.RequireDigit = false;//密码是否需要数字 opt.Password.RequireLowercase = false;//密码是否需要小写字符 opt.Password.RequireUppercase = false;//密码是否需要大写字符 opt.Password.RequireNonAlphanumeric = false;//密码是否需要非字母数字的字符 opt.Password.RequiredLength = 6;//密码长度 opt.Tokens.PasswordResetTokenProvider = TokenOptions.DefaultEmailProvider;//密码重置令牌,使用默认的邮箱令牌提供程序来生成和验证令牌。此提供程序通常与用户邮箱关联,生成的令牌会通过邮件发送给用户,保证用户通过邮件接收密码重置链接。 opt.Tokens.EmailConfirmationTokenProvider = TokenOptions.DefaultEmailProvider;//配置邮箱确认令牌(Email Confirmation Token)的生成和验证所使用的提供程序(Provider) }); var idBuilder = new IdentityBuilder(typeof(AspNetUsers), typeof(Role), builder.Services); idBuilder.AddEntityFrameworkStores<MyDbContext>() .AddDefaultTokenProviders().AddUserManager<UserManager<AspNetUsers>>() .AddRoleManager<RoleManager<Role>>(); builder.Services.Configure<JwtSettings>(builder.Configuration.GetSection("JwtSettings")); // 添加JWT认证 // 3. 认证服务配置(来自ServiceExtensions) builder.Services.ConfigureJwtAuthentication(builder.Configuration); // 扩展方法ServiceExtensions.cs // 4. 授权策略配置(来自ServiceExtensions) builder.Services.ConfigureAuthorizationPolicies(); // 扩展方法ServiceExtensions.cs // 5. 注册应用服务 builder.Services.AddScoped<IUserRepository, UserRepositor>(); builder.Services.AddScoped<IAuthService, AuthService>(); builder.Services.AddControllers(); // Learn more about configuring Swagger/OpenAPI at https://aka.ms/aspnetcore/swashbuckle builder.Services.AddEndpointsApiExplorer(); builder.Services.AddSwaggerGen(); //跨域 builder.Services.AddCors(options => { options.AddPolicy("AllowAll", builder => builder.AllowAnyOrigin() .AllowAnyMethod() .AllowAnyHeader()); }); var app = builder.Build(); // Configure the HTTP request pipeline. if (app.Environment.IsDevelopment()) { app.UseSwagger(); app.UseSwaggerUI(); } app.UseHttpsRedirection(); app.UseAuthentication(); app.UseAuthorization(); app.UseCors("AllowAll"); app.MapControllers(); app.Run();
8、使用示例
-
AdminController.cs
在需要权限的Controller上添加特性:csharpusing JWTWebAPI.Entity; using Microsoft.AspNetCore.Authorization; using Microsoft.AspNetCore.Http; using Microsoft.AspNetCore.Mvc; namespace JWTWebAPI.Controllers { [Route("api/[controller]/[action]")] [ApiController] public class AdminController : ControllerBase { [HttpGet] [Authorize(Roles = "admin")] // 仅Admin角色可访问 public IActionResult GetAdminData() { return Ok("Admin data"); } [HttpGet] [Authorize(Policy = "AdminOnly")] // 使用策略 public IActionResult GetUserInfo() { return Ok("User info"); } } }
9、高级权限控制
1)实现自定义策略处理器:
-
PermissionHandler.cs
csharpusing JWTWebAPI.Entity; using Microsoft.AspNetCore.Authorization; namespace JWTWebAPI.Extensions { public class PermissionHandler : AuthorizationHandler<PermissionRequirement> { protected override Task HandleRequirementAsync(AuthorizationHandlerContext context, PermissionRequirement requirement) { var permissions = context.User.FindFirst(c => c.Type == "permissions") ?.Value.Split(','); if (permissions?.Any(p => p == requirement.Permission) == true) { context.Succeed(requirement); } return Task.CompletedTask; } } }
-
PermissionRequirement.cs
csharpusing Microsoft.AspNetCore.Authorization; namespace JWTWebAPI.Entity { public class PermissionRequirement: IAuthorizationRequirement { public string Permission { get; set; } public PermissionRequirement(string permission) { Permission = permission; } } }
2)注册服务及定义策略
-
Program.cs
csharp//注册 builder.Services.AddSingleton<IAuthorizationHandler, PermissionHandler>(); //定义策略(可放在扩展方法中:ServiceExtensions.cs中的ConfigureAuthorizationPolicies方法中) builder.Services.AddAuthorization(options => { options.AddPolicy("UpdateValidate", policy => policy.Requirements.Add(new PermissionRequirement("profile.update"))); });
3)使用自定义策略
-
AdminController.cs
csharpusing JWTWebAPI.Entity; using Microsoft.AspNetCore.Authorization; using Microsoft.AspNetCore.Http; using Microsoft.AspNetCore.Mvc; namespace JWTWebAPI.Controllers { [Route("api/[controller]/[action]")] [ApiController] public class AdminController : ControllerBase { [HttpGet] [Authorize(Policy = "UpdateValidate")] public IActionResult GetMessage() { return Ok("自定义验证"); } } }
五、关键安全配置
-
密钥安全 :
- 生产环境不要将密钥放在appsettings.json中
- 使用Azure Key Vault 或环境变量存储密钥
csharpbuilder.Configuration.AddEnvironmentVariables();
-
HTTPS重定向 :
csharpapp.UseHttpsRedirection();
-
CORS配置 :
csharpservices.AddCors(options => { options.AddPolicy("ApiPolicy", builder => { builder.WithOrigins("https://your-frontend.com") .AllowAnyHeader() .AllowAnyMethod() .AllowCredentials(); }); });
六、完整流程说明
- 用户通过/api/auth/login获取JWT Token
- 后续请求在Header中添加Authorization: Bearer {token}
- 系统自动验证Token有效性
- 根据Controller/Action上的授权策略进行权限验证
总结
通过以上实现,可以构建灵活、可扩展的权限控制系统,满足以下场景:
- 基于资源的细粒度控制
- 动态权限管理
- 多条件组合授权