ABP - JWT 鉴权(JWT Authentication)[AbpJwtBearerModule、JwtBearerOptions]
JWT 鉴权(JWT Authentication)
核心辅助类:
AbpJwtBearerModule:JWT集成模块。JwtBearerOptions:JWT配置选项。
JWT(JSON Web Token)是ABP框架中常用的无状态鉴权方案,核心作用是“让客户端携带Token访问接口,服务器验证Token合法性”,避免每次请求都传递账号密码。以下结合AbpJwtBearerModule和JwtBearerOptions,用零基础能懂的例子讲解配置与使用。
一、JWT鉴权的核心流程(通俗理解)
可以把JWT想象成“电子门禁卡”:
- 客户端登录:用户输入账号密码,服务器验证通过后,生成一张“门禁卡”(JWT Token)并返回给客户端;
- 客户端访问接口:每次请求时,客户端在请求头里带上“门禁卡”(
Authorization: Bearer 你的Token); - 服务器验证:服务器收到请求后,先检查“门禁卡”是否有效(是否伪造、是否过期),有效则允许访问,无效则拒绝。
二、核心类说明
| 类名 | 作用 | 通俗理解 |
|---|---|---|
AbpJwtBearerModule |
ABP集成JWT的核心模块,提供JWT验证能力 | 相当于“门禁系统的基础框架”,必须引入才能用JWT |
JwtBearerOptions |
JWT的配置选项(如Token密钥、有效期) | 相当于“门禁卡的制作规则”(用什么密钥加密、卡多久过期) |
三、实操步骤:实现JWT鉴权
ABP实现JWT鉴权分3步:引入模块→配置JWT→使用Token访问接口,下面完整演示。
步骤1:引入JWT模块(基础准备)
首先在项目的核心模块(如MyAppWebModule)中,通过[DependsOn]引入AbpJwtBearerModule,开启JWT功能。
using Volo.Abp.Modularity;
using Volo.Abp.AspNetCore.Authentication.JwtBearer; // 引入JWT模块
// 声明依赖JWT模块
[DependsOn(
typeof(AbpAspNetCoreModule),
typeof(AbpJwtBearerModule) // 关键:引入JWT集成模块
)]
public class MyAppWebModule : AbpModule
{
// 后续配置JWT的代码写在这里
}
步骤2:配置JWT(核心步骤)
在模块的ConfigureServices方法中,通过JwtBearerOptions配置JWT的“制作规则”,比如密钥、有效期、发行人等。
完整配置代码
using Microsoft.AspNetCore.Authentication.JwtBearer;
using Microsoft.IdentityModel.Tokens;
using System.Text;
public override void ConfigureServices(ServiceConfigurationContext context)
{
var services = context.Services;
// 1. 配置JWT鉴权
services.AddAuthentication(JwtBearerDefaults.AuthenticationScheme) // 指定默认鉴权方案为JWT
.AddJwtBearer(options =>
{
// 2. 配置JWT验证规则(JwtBearerOptions核心参数)
options.TokenValidationParameters = new TokenValidationParameters
{
// 2.1 必须验证的内容(确保Token合法)
ValidateIssuer = true, // 是否验证“发行人”(谁发的Token)
ValidIssuer = "MyApp", // 合法的发行人(自定义,要和生成Token时一致)
ValidateAudience = true, // 是否验证“受众”(Token发给谁)
ValidAudience = "MyApp.Users", // 合法的受众(自定义,要和生成Token时一致)
ValidateLifetime = true, // 是否验证Token有效期(避免用过期的Token)
ValidateIssuerSigningKey = true, // 是否验证“签名密钥”(避免Token被伪造)
// 2.2 签名密钥(核心!必须和生成Token时用的密钥一致,且要足够复杂)
IssuerSigningKey = new SymmetricSecurityKey(
Encoding.UTF8.GetBytes("MyApp_JWT_Secret_Key_123456") // 密钥字符串(生产环境要换成更长的随机字符串)
),
// 2.3 可选配置:Token过期后的容错时间(允许30秒内的过期Token继续使用,避免网络延迟问题)
ClockSkew = TimeSpan.FromSeconds(30)
};
// 3. 可选:配置Token在请求中的传递方式(默认从请求头的Authorization字段获取)
// 若客户端用其他方式传递(如Query参数),可在这里扩展
options.Events = new JwtBearerEvents
{
// 示例:从Query参数(?token=xxx)获取Token(适合移动端或特殊场景)
OnMessageReceived = context =>
{
var token = context.Request.Query["token"];
if (!string.IsNullOrEmpty(token))
{
context.Token = token; // 将Query中的token赋值给上下文,供后续验证
}
return Task.CompletedTask;
}
};
});
// 4. 其他配置(如启用授权中间件)
services.AddAuthorization();
}
// 5. 在应用初始化时启用鉴权和授权中间件
public override void OnApplicationInitialization(ApplicationInitializationContext context)
{
var app = context.GetApplicationBuilder();
// 必须在UseRouting之后、UseEndpoints之前添加
app.UseAuthentication(); // 启用鉴权(验证Token)
app.UseAuthorization(); // 启用授权(验证用户是否有权限访问接口)
}
步骤3:生成JWT Token(登录接口)
配置好JWT后,需要一个“登录接口”来生成Token。通常在AccountAppService中实现,通过IJwtTokenGenerator生成Token。
登录接口代码
using Volo.Abp.Application.Services;
using Volo.Abp.Identity;
using Volo.Abp.Security.Claims;
using Microsoft.AspNetCore.Identity;
using System.Security.Claims;
public class AccountAppService : ApplicationService, IAccountAppService
{
// 注入生成Token的工具
private readonly IJwtTokenGenerator _jwtTokenGenerator;
// 注入ABP内置的用户管理器(用于验证账号密码)
private readonly UserManager<IdentityUser> _userManager;
public AccountAppService(IJwtTokenGenerator jwtTokenGenerator, UserManager<IdentityUser> userManager)
{
_jwtTokenGenerator = jwtTokenGenerator;
_userManager = userManager;
}
// 登录接口:输入账号密码,返回Token
public async Task<LoginResultDto> LoginAsync(LoginInputDto input)
{
// 1. 验证账号是否存在
var user = await _userManager.FindByNameAsync(input.UserName);
if (user == null)
{
throw new UserFriendlyException("用户名不存在!");
}
// 2. 验证密码是否正确
var passwordValid = await _userManager.CheckPasswordAsync(user, input.Password);
if (!passwordValid)
{
throw new UserFriendlyException("密码错误!");
}
// 3. 获取用户的角色(可选:把角色信息放到Token中,方便后续权限判断)
var roles = await _userManager.GetRolesAsync(user);
// 4. 生成JWT Token(核心:用IJwtTokenGenerator按配置生成Token)
var token = await _jwtTokenGenerator.GenerateTokenAsync(
userId: user.Id.ToString(), // 用户ID
userName: user.UserName, // 用户名
email: user.Email, // 邮箱(可选,放到Token的“声明”中)
roles: roles // 角色(可选,放到Token的“声明”中)
);
// 5. 返回Token给客户端
return new LoginResultDto
{
Token = token,
ExpirationTime = DateTime.Now.AddHours(2) // Token有效期(2小时,要和配置一致)
};
}
}
// 登录输入DTO(客户端传参)
public class LoginInputDto
{
public string UserName { get; set; } // 用户名
public string Password { get; set; } // 密码
}
// 登录返回DTO(给客户端的结果)
public class LoginResultDto
{
public string Token { get; set; } // JWT Token
public DateTime ExpirationTime { get; set; } // Token过期时间
}
步骤4:用Token访问接口(客户端操作)
客户端拿到Token后,每次访问需要鉴权的接口时,都要在请求头中携带Token。
1. 前端示例(Axios)
// 1. 登录获取Token(调用上面的Login接口)
async function login(userName, password) {
const response = await axios.post('/api/app/account/login', {
userName: userName,
password: password
});
const token = response.data.token;
// 把Token存到本地存储(localStorage),方便后续使用
localStorage.setItem('myAppToken', token);
return token;
}
// 2. 携带Token访问接口(比如获取用户信息)
async function getUserInfo() {
const token = localStorage.getItem('myAppToken');
const response = await axios.get('/api/app/user/info', {
headers: {
// 关键:在Authorization头中携带Token,格式为“Bearer + 空格 + Token”
'Authorization': `Bearer ${token}`
}
});
return response.data;
}
2. 接口鉴权控制(服务端)
在需要鉴权的接口上添加[Authorize]特性,未携带有效Token的请求会被拒绝:
using Microsoft.AspNetCore.Authorization;
// 添加[Authorize]特性:只有携带有效Token的请求才能访问
[Authorize]
public class UserAppService : ApplicationService
{
// 需鉴权的接口:获取当前登录用户信息
public async Task<UserDto> GetCurrentUserInfoAsync()
{
// 通过CurrentUser获取当前登录用户的信息(Token中包含的用户ID、角色等)
var userId = CurrentUser.Id;
var userName = CurrentUser.UserName;
// 后续查询用户信息并返回...
}
}
四、关键配置详解
1. JwtBearerOptions.TokenValidationParameters核心参数
| 参数名 | 作用 | 注意事项 |
|---|---|---|
ValidateIssuer |
是否验证Token的“发行人”(Issuer) | 必须设为true,避免其他系统发的Token混入 |
ValidIssuer |
合法的发行人名称 | 要和IJwtTokenGenerator生成Token时的发行人一致 |
ValidateAudience |
是否验证Token的“受众”(Audience) | 必须设为true,确保Token是发给当前系统的 |
ValidAudience |
合法的受众名称 | 要和生成Token时的受众一致 |
ValidateLifetime |
是否验证Token有效期 | 必须设为true,避免过期Token被滥用 |
IssuerSigningKey |
验证Token签名的密钥 | 密钥要足够复杂(建议32位以上随机字符串),生产环境要存在配置文件中,不要硬编码 |
2. Token的“声明”(Claims)
Token中可以包含“声明”(比如用户ID、角色、邮箱),这些信息会在服务器验证Token后解析出来,通过CurrentUser获取:
// 在需要的地方通过CurrentUser获取Token中的声明信息
public async Task DoSomethingAsync()
{
var userId = CurrentUser.Id; // 获取用户ID(来自Token的声明)
var userName = CurrentUser.UserName; // 获取用户名
var isAdmin = CurrentUser.IsInRole("Admin"); // 判断是否为Admin角色(来自Token的角色声明)
}
五、新手避坑指南
-
密钥硬编码问题:示例中密钥是硬编码的
MyApp_JWT_Secret_Key_123456,生产环境必须放到配置文件(如appsettings.json)中,避免泄露;// appsettings.json中配置密钥 "Jwt": { "SecretKey": "Your_Super_Long_Random_Secret_Key_1234567890", "Issuer": "MyApp", "Audience": "MyApp.Users", "ExpirationHours": 2 }然后在代码中读取配置:
var jwtConfig = context.Services.GetConfiguration().GetSection("Jwt"); options.TokenValidationParameters.IssuerSigningKey = new SymmetricSecurityKey( Encoding.UTF8.GetBytes(jwtConfig["SecretKey"]) ); -
Token有效期不一致:生成Token时的有效期(如2小时)要和
JwtBearerOptions中的配置一致,避免Token没过期却被判定为无效; -
忘记启用中间件:必须在
OnApplicationInitialization中添加app.UseAuthentication()和app.UseAuthorization(),且顺序不能错(先鉴权,再授权)。
总结
- 核心流程:登录生成Token→客户端携带Token请求→服务器验证Token→允许/拒绝访问;
- 关键类:
AbpJwtBearerModule(开启JWT)、JwtBearerOptions(配置验证规则);

浙公网安备 33010602011771号