ASP.NET Core 中JWT的基本使用

文章目录


前言

ASP.NET Core Web API 中实现基于JWTRBAC(基于角色的访问控制)。

一、JWT与RBAC

JWT (JSON Web Token)与 RBAC (基于角色的访问控制,Role-Based Access Access Control)结合使用时,是一种通过令牌(Token)传递用户角色信息,并基于角色实现权限管理的安全机制。

二、JWT 的作用

  • JWT 是一种紧凑的、自包含的令牌格式,通常用于身份验证和授权。其结构分为三部分:
    • Header :算法和令牌类型(如 HS256RSA)。
    • Payload:存放用户信息(如用户ID、角色)和声明(如过期时间)。
    • Signature:对前两部分的签名,确保令牌未被篡改。

三、RBAC 的核心思想

  • RBAC 通过角色管理权限,而非直接赋予用户权限:
    • 角色(Role) :定义一组权限(如 adminuser)。
    • 权限(Permission) :资源或操作(如 read:data、delete:user)。
    • 用户被分配角色,间接获得权限。

四、使用

1、配置文件 (appsettings.json)

  1. appsettings.json

    csharp 复制代码
    {
      "JwtSettings": {
    	  "Issuer": "yourdomain.com",
    	  "Audience": "yourapp",
    	  "SecretKey": "YourSuperSecretKeyAtLeast32CharactersLong",
    	  "ExpirationMinutes": 60,
    	  "RefreshTokenExpirationDays": 7
    	}
      "AllowedHosts": "*"
    }

2、JWT配置模型 (Entity/JwtSettings.cs)

  1. JwtSettings.cs

    csharp 复制代码
    namespace 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)

  1. ServiceExtensions.cs

    csharp 复制代码
    using 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、用户仓库接口服务

  1. Interfaces/IUserRepository.cs

    csharp 复制代码
    using 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);
        }
    }
  2. Repositories/UserRepository.cs

    csharp 复制代码
    using 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)

  1. IAuthService.cs

    csharp 复制代码
    using JWTWebAPI.Entity;
    
    namespace JWTWebAPI.Interface
    {
        public interface IAuthService
        {
            Task<AuthResult> Authenticate(string username, string password);
            Task<AuthResult> RefreshToken(string token, string refreshToken);
        }
    }
  2. AuthService.cs

    csharp 复制代码
    using 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);
            }
        }
    }
  3. AuthResult.cs

    csharp 复制代码
    namespace JWTWebAPI.Entity
    {
        public class AuthResult
        {
            public string Token { get; set; }
            public string RefreshToken { get; set; }
            public int ExpiresIn { get; set; }
        }
    }

6、控制器(验证登录权限,生成Token)

  1. AuthController.cs

    csharp 复制代码
    using 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);
            }
        }
    } 
  2. LoginModel.cs

    csharp 复制代码
    namespace JWTWebAPI.Entity
    {
        public class LoginModel
        {
            public string Username { get; set; } = string.Empty;
            public string Password { get; set; } = string.Empty;
        }
    }
  3. RefreshTokenRequest.cs

    csharp 复制代码
    public class RefreshTokenRequest
    {
        [Required] public string Token { get; set; }
        [Required] public string RefreshToken { get; set; }
    }

7、注册服务

  1. Program.cs

    csharp 复制代码
    using 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、使用示例

  1. AdminController.cs
    在需要权限的Controller上添加特性:

    csharp 复制代码
    using 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)实现自定义策略处理器:

  1. PermissionHandler.cs

    csharp 复制代码
    using 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;
            }
        }
    }
  2. PermissionRequirement.cs

    csharp 复制代码
    using Microsoft.AspNetCore.Authorization;
    
    namespace JWTWebAPI.Entity
    {
        public class PermissionRequirement: IAuthorizationRequirement
        {
            public string Permission { get; set; }
    
            public PermissionRequirement(string permission)
            {
                Permission = permission;
            }
        }
    }

2)注册服务及定义策略

  1. 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)使用自定义策略

  1. AdminController.cs

    csharp 复制代码
    using 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环境变量存储密钥
    csharp 复制代码
    builder.Configuration.AddEnvironmentVariables();
  • HTTPS重定向

    csharp 复制代码
    app.UseHttpsRedirection();
  • CORS配置

    csharp 复制代码
    services.AddCors(options =>
    {
        options.AddPolicy("ApiPolicy", builder =>
        {
            builder.WithOrigins("https://your-frontend.com")
                .AllowAnyHeader()
                .AllowAnyMethod()
                .AllowCredentials();
        });
    });

六、完整流程说明

  1. 用户通过/api/auth/login获取JWT Token
  2. 后续请求在Header中添加Authorization: Bearer {token}
  3. 系统自动验证Token有效性
  4. 根据Controller/Action上的授权策略进行权限验证

总结

通过以上实现,可以构建灵活、可扩展的权限控制系统,满足以下场景:

  • 基于资源的细粒度控制
  • 动态权限管理
  • 多条件组合授权
相关推荐
MoFe114 分钟前
【.net core】SkiaSharp 如何在Linux上实现
linux·服务器·.netcore
[email protected]18 分钟前
Asp.Net Core 通过JWT版本号实现JWT无法提前撤回的问题
后端·中间件·asp.net·.netcore
ademen27 分钟前
spring4第4课-ioc控制反转-详解如何注入参数
java·后端·spring
Hello-Mr.Wang42 分钟前
使用electron创建应用程序的基础步骤
后端·electron
江沉晚呤时1 小时前
深入了解 C# 异步编程库 AsyncEx
java·前端·数据库·c#·.netcore
KENYCHEN奉孝2 小时前
Weather app using Django - Python
后端·python·django
[email protected]3 小时前
Asp.Net Core 托管服务
后端·asp.net·.netcore
我的golang之路果然有问题3 小时前
快速了解 GO之接口解耦
开发语言·笔记·后端·学习·golang
不争先.4 小时前
为什么在我的Flask里面有两个路由,但是在网页里有一个却不能正确访问到智能体
后端·python·flask
长勺5 小时前
Spring Boot自动装配原理
java·spring boot·后端