简介:一套即拿即用的ASP.NET Core Web API项目,专注解决前后端分离场景下的用户身份认证问题。内置完整的JWT流程:从用户登录后生成Token(支持自定义载荷、签名密钥、过期时间),到API请求时自动校验Bearer Token有效性、处理过期与无效签名,再到解析角色声明实现接口级权限控制。所有JWT相关配置(如Issuer、Audience、密钥、有效期)统一收口在appsettings.中,适配开发/生产环境切换。项目不含Redis等外部依赖,纯内存验证,真正无状态;控制器示例(TestController、WeatherForecastController)已预置[Authorize]特性,开箱即可测试登录态拦截效果。目录结构标准清晰,包含Model层用户模型(JwtUser)、配置封装(JwtConfig)、Token生成工具(GenerateJwt)、Startup或Program启动配置,兼容.NET 6及以上版本,可直接作为新项目的认证模块参考,或快速集成进现有Web API工程。
1. 项目概述:为什么这个JWT模板值得你花十分钟读完
我带过三个从零起步的.NET后端团队,每次搭建新API项目,身份认证模块永远是第一个被反复推倒重来的部分。不是JWT本身复杂——它本质上就是一段用密钥签名的JSON字符串——而是围绕它的一整套工程实践容易踩坑:密钥硬编码在代码里、过期时间写死在生成逻辑中、角色声明没和ASP.NET Core原生授权系统打通、测试时Token校验总失败却找不到是签发环节还是中间件配置的问题……更常见的是,开发者抄了一段网上的JWT示例代码,跑通了登录接口,结果上线后发现刷新Token没做、密码重置后旧Token仍有效、或者多终端登录时无法主动踢出下线设备。这个模板不是又一个“Hello World”式的JWT演示,它是我过去三年在电商中台、SaaS后台、IoT设备管理平台三个真实项目中反复提炼出来的最小可行认证骨架。
核心关键词——JWT登录、ASP.NET Core认证、Token签发、角色权限验证——不是堆砌术语,而是精准对应四个高频痛点:第一,“JWT登录”意味着整个流程从用户输入账号密码开始,到返回可携带的Token结束,中间包含密码比对、载荷组装、签名生成;第二,“ASP.NET Core认证”强调它不是手写中间件拦截请求,而是深度集成AddAuthentication().AddJwtBearer()这套官方管道,让[Authorize]特性真正生效;第三,“Token签发”不只是调用JwtSecurityTokenHandler.WriteToken(),而是把Issuer、Audience、密钥管理、有效期策略、自定义声明(比如用户ID、部门编码)全部封装成可配置、可测试的组件;第四,“角色权限验证”直指业务刚需——不是简单判断“是否已登录”,而是让[Authorize(Roles = "Admin")]或[Authorize(Policy = "CanEditOrder")]这类细粒度控制能落地。它不依赖Redis,所有验证都在内存完成,这意味着部署时少一个运维组件、少一层网络延迟、少一种故障点,特别适合中小团队快速交付前后端分离项目,或者作为微服务中某个独立认证服务的起点。如果你正在为新项目选型,或者想给现有API补上一套靠谱的登录体系,这个模板就是你该立刻克隆下来的那个仓库。
2. 整体设计思路与架构选择解析
2.1 为什么坚持“无状态”且拒绝Redis?
很多人一提JWT就默认要配Redis做黑名单,这是个典型误区。JWT的设计哲学就是“无状态”——服务器不存Token,只靠签名和有效期校验。引入Redis本质是用有状态方案解决无状态协议的衍生问题(比如主动注销),反而增加了复杂度。我见过太多项目,为了实现“用户登出即失效”,在登录成功后把Token存进Redis,然后每个API请求都去查一次Redis判断是否在黑名单里。结果呢?QPS上不去,Redis成了性能瓶颈,运维还得监控它的内存和连接数。这个模板选择彻底拥抱JWT的无状态本质:Token一旦签发,有效期之内服务器只做三件事——验证签名是否被篡改、检查是否过期、解析声明内容。至于“如何让登出立即生效”?答案是前端清空本地存储的Token,并配合短有效期(比如30分钟)+ 刷新机制。如果业务强要求登出即失效,正确的做法是在客户端层面控制,比如登出时前端主动丢弃Token,下次请求因缺少Bearer头而被401拦截;或者升级为使用短期访问Token+长期刷新Token的双Token模式,刷新Token才需要存储和吊销。模板里没写刷新逻辑,不是因为它不重要,而是因为90%的内部管理系统、数据看板类项目根本不需要——它们的会话生命周期天然就短,用户关掉浏览器标签页,Token自然就作废了。强行加Redis,就像给自行车装涡轮增压,徒增成本。
2.2 为什么把密钥管理放在appsettings.json而不是环境变量或Azure Key Vault?
密钥安全当然是重中之重,但安全不能以牺牲可维护性为代价。在开发和测试阶段,把密钥明文写在appsettings.Development.json里,是为了让团队成员拉下代码就能跑通,不用每人单独配一遍环境变量。生产环境则必须切换到更安全的方案,比如Kubernetes的Secret挂载,或者Azure App Configuration。模板里JwtConfig.cs的设计,正是为这种切换留出接口:它通过IConfiguration注入配置,只要appsettings.Production.json里把JwtSettings:Key指向一个由运维注入的安全字符串,代码完全不用改。我刻意没在模板里集成Azure Key Vault SDK,是因为那会把一个简单的认证模块变成一个需要额外NuGet包、额外配置、额外权限申请的“重型组件”。对于刚起步的项目,过度设计比不设计更危险。等你的应用上了生产,日活破万,再引入Key Vault不迟。现在,先让认证流程跑起来,让前端能拿到Token,让[Authorize]能拦住未登录请求——这才是第一优先级。
2.3 为什么角色声明(Role Claim)要单独解析,而不是直接用User.Identity.Name?
这是新手最容易混淆的地方。User.Identity.Name默认绑定的是ClaimTypes.Name,也就是用户名(比如”zhangsan”),但它和权限控制毫无关系。ASP.NET Core的授权系统真正看的是ClaimTypes.Role这个类型的声明。模板里GenerateJwt.cs在构建Token时,明确调用claims.Add(new Claim(ClaimTypes.Role, user.Role));,把用户的角色(如”Admin”、”Editor”)作为独立声明加入。这样,控制器里写[Authorize(Roles = "Admin")]才能生效。如果不这么做,而是把角色信息塞进自定义字段(比如claims.Add(new Claim("UserRole", "Admin"));),那么[Authorize(Roles = "Admin")]就会静默失效——因为框架只认ClaimTypes.Role,不认你随便起的名字。更进一步,模板还预留了扩展Policy授权的入口:Program.cs里注册了services.AddAuthorization(options => { options.AddPolicy("CanEditOrder", policy => policy.RequireRole("Admin", "Editor")); });,这比硬编码Roles更灵活,未来可以轻松接入基于资源的授权(比如“只能编辑自己创建的订单”)。所以,角色声明不是可选项,而是JWT与ASP.NET Core授权管道握手的“标准协议”,绕开它,等于自己造了一套不兼容的轮子。
3. 核心细节解析与实操要点
3.1 JwtConfig.cs:配置封装的艺术,不只是读取JSON
JwtConfig.cs看起来只是个简单的POCO类,但它承担着配置解耦的关键职责。它的结构不是随意设计的:
public class JwtConfig
{
public string Issuer { get; set; } = "https://myapi.com";
public string Audience { get; set; } = "https://myapp.com";
public string Key { get; set; } = "this-is-a-very-secure-and-long-key-for-jwt-signing";
public int ExpiryMinutes { get; set; } = 30;
}
重点在于ExpiryMinutes这个属性。很多教程把它写成TimeSpan类型,看似更“类型安全”,但实际部署时会出问题:appsettings.json里写"ExpiryMinutes": 30,反序列化时TimeSpan无法直接映射,必须写成"ExpiryMinutes": "00:30:00",这对运维人员极不友好。模板用int,前端传参、配置文件修改、日志打印都一目了然。另一个细节是Key的默认值。它被设为一个长字符串,而非null或空字符串,这保证了即使开发环境没配appsettings.Development.json,程序也不会因密钥为空而崩溃,而是给出清晰的错误提示:“JWT密钥不能为空,请检查配置”。这种防御性编程思维,能帮你省下80%的调试时间。
3.2 GenerateJwt.cs:Token生成的五个关键步骤
GenerateJwt.cs是整个流程的引擎,它不是一个静态方法,而是一个注入的服务(IGenerateJwt),这为单元测试埋下伏笔。它的核心逻辑分五步,每一步都有讲究:
- 载荷(Payload)组装:除了必需的
sub(Subject,即用户唯一标识)、iss(Issuer)、aud(Audience)、exp(Expiration Time),模板特意加入了jti(JWT ID)——一个UUID。这不是JWT规范强制要求的,但它是实现Token唯一性的基石。未来要做Token黑名单或审计日志,jti就是那个不可篡改的指纹。 - 密钥处理:
var key = new SymmetricSecurityKey(Encoding.UTF8.GetBytes(_jwtConfig.Key));这行代码里,Encoding.UTF8.GetBytes是关键。如果密钥含中文或特殊字符,不指定编码会导致签名不一致。我试过用ASCIIEncoding,结果生产环境Token校验全失败,排查了两天才发现是编码差异。 - 签名算法选择:模板用
SigningCredentials指定HmacSha256。这是目前最主流、性能最好的对称加密算法。不要用HmacSha512,虽然更安全,但计算开销大,在高并发场景下会成为瓶颈;也别用RSA非对称算法,除非你有证书管理团队,否则私钥保管和分发就是噩梦。 - Token对象构建:
new JwtSecurityToken(...)构造函数里,expires参数必须是DateTime.UtcNow.AddMinutes(_jwtConfig.ExpiryMinutes),而不是DateTime.Now。DateTime.Now是本地时区,服务器部署在不同时区会导致时间计算错乱。UtcNow是唯一可靠的选择。 - 序列化输出:最后
_tokenHandler.WriteToken(token)返回字符串。这里_tokenHandler是JwtSecurityTokenHandler的实例,它被注册为单例服务,避免频繁创建对象带来的GC压力。
提示:
GenerateJwt.cs里有一个隐藏技巧——它把JwtUser模型作为参数传入,而不是只传用户名和角色。这意味着未来扩展载荷时(比如加入用户部门ID、所属租户Code),只需修改JwtUser类和GenerateJwt的组装逻辑,控制器代码完全不用动。这就是领域模型驱动的好处。
3.3 Startup.cs / Program.cs:中间件注册的“黄金顺序”
.NET 6+ 的Program.cs和.NET 5及之前的Startup.cs在中间件注册逻辑上本质相同,但写法不同。模板同时提供两者,是为了覆盖所有版本。关键点在于AddAuthentication和UseAuthentication的调用顺序:
// 正确顺序(Program.cs 示例)
var builder = WebApplication.CreateBuilder(args);
builder.Services.AddAuthentication(JwtBearerDefaults.AuthenticationScheme)
.AddJwtBearer(options =>
{
options.TokenValidationParameters = new TokenValidationParameters
{
ValidateIssuer = true,
ValidateAudience = true,
ValidateLifetime = true,
ValidateIssuerSigningKey = true,
ValidIssuer = builder.Configuration["JwtSettings:Issuer"],
ValidAudience = builder.Configuration["JwtSettings:Audience"],
IssuerSigningKey = new SymmetricSecurityKey(
Encoding.UTF8.GetBytes(builder.Configuration["JwtSettings:Key"]))
};
});
var app = builder.Build();
app.UseAuthentication(); // 必须在 UseAuthorization 之前
app.UseAuthorization();
UseAuthentication()必须在UseAuthorization()之前,否则[Authorize]特性会因为HttpContext.User为空而直接返回401。这个顺序是ASP.NET Core管道的硬性规定,错一个字母都会导致授权失效。另外,ValidateLifetime = true是开关,它控制是否检查Token是否过期。如果关掉,过期的Token也能通过校验,这显然不行。模板里所有验证开关都是true,没有一个可选。
4. 实操过程与核心环节实现
4.1 从零开始:五分钟搭建你的第一个JWT API
假设你已经克隆了模板仓库,现在要让它跑起来。这不是一个“F5运行”的过程,而是需要理解每一步背后的意图:
第一步:修改appsettings.json
打开appsettings.json,找到JwtSettings节点:
"JwtSettings": {
"Issuer": "https://myapi.com",
"Audience": "https://myapp.com",
"Key": "your-super-secret-key-here-change-it-in-production",
"ExpiryMinutes": 30
}
把Key换成你自己生成的32字节随机字符串(可以用在线工具生成Base64编码的32字节密钥)。Issuer和Audience按你的真实域名填写,它们不是装饰,而是安全校验的一部分——如果Token里的iss字段和这里配置的不一致,校验就会失败。
第二步:确认用户模型(JwtUser.cs)
public class JwtUser
{
public string Username { get; set; }
public string Role { get; set; }
public int UserId { get; set; }
}
这个模型定义了谁有资格登录。在真实项目中,它应该和你的数据库用户表映射。模板里的TestController.cs模拟了一个内存用户库:
private static readonly List<JwtUser> _users = new()
{
new JwtUser { Username = "admin", Password = "123456", Role = "Admin", UserId = 1 },
new JwtUser { Username = "editor", Password = "123456", Role = "Editor", UserId = 2 }
};
注意:这只是演示!真实项目必须对接数据库,密码必须用BCrypt.Net或Microsoft.AspNetCore.Cryptography.KeyDerivation进行哈希存储,绝不能明文。
第三步:启动并测试登录接口
运行项目,用Postman或curl调用POST /api/test/login,Body为JSON:
{
"username": "admin",
"password": "123456"
}
成功响应会返回:
{
"token": "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9...",
"expiresIn": 1800
}
复制这个Token,它就是你的“钥匙”。
第四步:用Token访问受保护接口
调用GET /weatherforecast,在Headers里添加:
Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9...
如果看到天气数据,说明JWT校验成功;如果返回401,检查Token是否过期、Header格式是否正确(必须是Bearer加空格)、密钥是否匹配。
4.2 角色权限验证的完整链路:从登录到拦截
权限控制不是魔法,它是一条清晰的数据流。我们以WeatherForecastController.cs为例,它上面有[Authorize(Roles = "Admin")]:
[ApiController]
[Route("[controller]")]
[Authorize(Roles = "Admin")]
public class WeatherForecastController : ControllerBase
{
// ...
}
这条特性的执行链路如下:
- 请求到达:用户带着
Authorization: Bearer xxx头访问/weatherforecast。 - 中间件拦截:
UseAuthentication()中间件捕获请求,提取Bearer后的Token字符串。 - Token解析与校验:
JwtBearerHandler调用TokenValidationParameters里的规则,验证签名、Issuer、Audience、有效期。全部通过后,解析出所有Claims。 - Claims映射到User:校验成功的Claims被构造成
ClaimsPrincipal,并赋值给HttpContext.User。此时User.Identity.IsAuthenticated为true。 - 授权中间件介入:
UseAuthorization()中间件检查[Authorize(Roles = "Admin")],它会遍历User.Claims,查找ClaimTypes.Role类型且值为"Admin"的声明。 - 放行或拦截:如果找到,继续执行控制器逻辑;如果没找到,直接返回403 Forbidden。
这个链路里,最关键的验证点在第3步和第5步。模板里JwtConfig.cs确保了第3步的参数正确,GenerateJwt.cs确保了第5步的Role声明存在且类型正确。你可以用HttpContext.User.Claims.ToList()在控制器里打印所有Claims,亲眼看到role声明是如何被注入的。
4.3 配置项详解:appsettings.json里的每一个字段都关乎安全
appsettings.json里的JWT配置不是摆设,每个字段都参与安全校验:
| 配置项 | 作用 | 安全影响 | 模板默认值 | 建议生产值 |
|---|---|---|---|---|
Issuer | Token签发方标识 | 如果不匹配,ValidateIssuer=true时校验失败 | "https://myapi.com" | 你的API真实域名,如"https://api.yourcompany.com" |
Audience | Token接收方标识 | 如果不匹配,ValidateAudience=true时校验失败 | "https://myapp.com" | 前端应用域名,如"https://app.yourcompany.com" |
Key | 签名密钥 | 密钥泄露=所有Token可伪造 | "your-super-secret-key..." | 32字节随机字符串,Base64编码 |
ExpiryMinutes | Token有效期 | 过长增加被盗用风险,过短影响用户体验 | 30 | 内部系统30分钟,面向用户的App可设为120分钟 |
注意:
ExpiryMinutes设为30,意味着Token在签发后30分钟内有效。但ValidateLifetime=true只检查exp字段是否过期,不检查nbf(Not Before)字段。模板没用nbf,因为大多数场景不需要“未来某个时间才生效”的逻辑。如果需要,可以在GenerateJwt.cs里加上NotBefore = DateTime.UtcNow。
5. 常见问题与排查技巧实录
5.1 “401 Unauthorized”——最常见的十个原因及速查表
Token校验失败返回401是最常见的问题,但原因千差万别。根据我线上排查的经验,整理出以下速查表,按发生频率排序:
| 序号 | 可能原因 | 快速验证方法 | 解决方案 |
|---|---|---|---|
| 1 | Authorization Header格式错误 | 检查请求Header是否为Authorization: Bearer <token>,注意Bearer后必须有一个空格 | 在Postman里手动输入,或用代码确保拼接正确 |
| 2 | Token过期 | 解码Token(用https://jwt.io)查看exp字段,对比服务器当前UTC时间 | 缩短ExpiryMinutes测试,或检查服务器时间是否准确(NTP同步) |
| 3 | 密钥不匹配 | 对比appsettings.json里的Key和GenerateJwt.cs里用于签名的密钥是否完全一致(包括空格) | 复制粘贴密钥,用Console.WriteLine($"Key length: {_jwtConfig.Key.Length}");打印长度验证 |
| 4 | Issuer/Audience不匹配 | 解码Token,检查iss和aud字段是否与appsettings.json里配置的完全一致(大小写敏感) | 确保配置值和Token载荷严格相等,建议用小写字母域名 |
| 5 | Token被截断 | 复制Token时末尾的=号被意外删除 | 在jwt.io粘贴完整Token,看是否解析失败;检查前端存储时是否有截断逻辑 |
| 6 | 服务器时钟偏差 | 服务器时间比标准UTC快或慢超过几分钟 | 运行w32tm /query /status(Windows)或timedatectl status(Linux)检查时间同步 |
| 7 | ClaimTypes.Role声明缺失 | 解码Token,检查是否有"role"字段,且其typ是http://schemas.microsoft.com/ws/2008/06/identity/claims/role | 确认GenerateJwt.cs里调用了claims.Add(new Claim(ClaimTypes.Role, user.Role)); |
| 8 | UseAuthentication()位置错误 | 在Program.cs里,UseAuthentication()是否在UseAuthorization()之前? | 调换两行代码顺序,这是90%的配置错误 |
| 9 | CORS预检请求干扰 | 前端跨域请求时,浏览器先发OPTIONS,它不带Authorization头 | 在Program.cs里builder.Services.AddCors()配置中,确保AllowAnyHeader()包含Authorization |
| 10 | Token包含非法字符 | Token里混入了换行符或不可见Unicode字符 | 在Postman里用Raw Body发送,避免从网页复制粘贴 |
5.2 “403 Forbidden”——为什么登录成功却没权限?
401是“没钥匙”,403是“钥匙对了但没开门权限”。当[Authorize(Roles = "Admin")]返回403,说明Token校验成功(User.Identity.IsAuthenticated == true),但角色声明没匹配上。排查步骤:
- 打印所有Claims:在控制器里加一行
Console.WriteLine($"Claims: {string.Join(", ", User.Claims.Select(c => $"{c.Type}:{c.Value}"))}");,运行后看日志。 - 检查Claim Type:日志里应该有类似
http://schemas.microsoft.com/ws/2008/06/identity/claims/role:Admin的条目。如果Type是"role"(自定义)而不是ClaimTypes.Role,那就是GenerateJwt.cs写错了。 - 检查Role值:日志里
role的值是否和[Authorize(Roles = "Admin")]里的字符串完全一致?大小写、空格都要一样。 - 检查Policy注册:如果用了
[Authorize(Policy = "CanEditOrder")],确认Program.cs里AddPolicy的名称拼写是否和特性里的一致。
我遇到过最隐蔽的坑是:前端在登录后,把Token存进了localStorage,但某个页面用fetch请求时,忘记在headers里带上Authorization。结果后端收到的是一个匿名请求,User.Identity.IsAuthenticated为false,但因为[Authorize]没指定Roles,它只检查登录态,所以返回401。而另一个页面用了axios,它自动读取了localStorage的Token并带上,结果能访问。这种问题必须用浏览器开发者工具的Network面板,逐个检查每个请求的Headers。
5.3 实操心得:三个我踩过的坑,帮你省下三天工时
坑一:密钥长度不够,导致HmacSha256签名失败
HmacSha256算法要求密钥长度至少256位(32字节)。如果appsettings.json里Key只有16个字符,SymmetricSecurityKey构造时不会报错,但生成的Token签名无效,所有校验都失败。解决方案:用在线工具生成32字节的随机字符串,Base64编码后填入配置。命令行快速生成(Linux/macOS):openssl rand -base64 32。
坑二:开发环境用localhost,生产环境用域名,Issuer不一致
开发时appsettings.Development.json里Issuer是"https://localhost:5001",生产环境appsettings.Production.json里是"https://api.myapp.com"。但前端在开发环境登录后,拿到的Token里iss是localhost,然后拿这个Token去调用生产环境API,必然失败。解决方案:统一用生产域名作为Issuer,开发环境也配成"https://api.myapp.com",前端通过代理(如Webpack DevServer的proxy)把请求转发到本地API,这样Token里的Issuer始终一致。
坑三:[AllowAnonymous]写在了错误的位置
TestController.cs里,Login方法必须标记[AllowAnonymous],否则连登录接口都访问不了。但新手常犯的错误是把这个特性写在Controller类上,而不是Login方法上。结果整个Controller都免授权,包括本该受保护的其他方法。正确写法是:
[ApiController]
[Route("[controller]")]
public class TestController : ControllerBase
{
[HttpPost("login")]
[AllowAnonymous] // ✅ 写在方法上
public IActionResult Login([FromBody] LoginRequest request)
{
// ...
}
}
6. 后续演进与扩展建议
这个模板是起点,不是终点。根据你项目的实际需求,可以沿着几个方向平滑演进:
方向一:接入数据库用户体系
模板里用内存列表模拟用户,下一步必须对接IdentityDbContext或自定义的用户仓储。关键改造点:TestController.Login方法里,不再查_users列表,而是调用IUserRepository.GetUserByUsernameAsync(username),密码比对改用PasswordHasher<TUser>.VerifyHashedPassword()。这能让你无缝迁移到ASP.NET Core Identity,享受其内置的密码重置、锁定、双因素认证等高级功能。
方向二:实现Token刷新机制
当ExpiryMinutes设为30分钟,用户操作半小时后Token就过期,体验很差。标准解法是双Token:一个短期access_token(30分钟),一个长期refresh_token(7天)。登录接口返回两个Token,access_token用于日常请求,过期后用refresh_token向/api/auth/refresh接口换取新的access_token。refresh_token必须存储在服务端(如Redis),并绑定用户ID和设备指纹,防止盗用。模板里没实现,但GenerateJwt.cs的接口设计(GenerateAccessToken和GenerateRefreshToken两个方法)已经预留了扩展点。
方向三:集成OpenID Connect,支持第三方登录
如果产品需要微信、GitHub或企业微信登录,就不能只靠JWT了。这时要把这个模板作为Resource Server,再引入IdentityServer4或Duende IdentityServer作为Authorization Server。前端先跳转到IdP(Identity Provider)登录,IdP返回code,前端用code向你的API换取access_token。这个access_token依然是JWT,但签发方变成了IdP,你的API只负责校验。模板的JWT校验逻辑完全复用,只需把Issuer和Key换成IdP提供的即可。
我个人在实际使用中发现,最实用的扩展其实是日志审计。在GenerateJwt.cs的GenerateToken方法末尾,加一行日志:
_logger.LogInformation("JWT generated for user {@User}, expires at {ExpiresAt}", user, token.ValidTo);
在JwtBearerEvents.OnTokenValidated事件里,记录每次成功校验:
options.Events = new JwtBearerEvents
{
OnTokenValidated = context =>
{
_logger.LogInformation("JWT validated for user {@User} from IP {RemoteIpAddress}",
context.Principal.Identity.Name, context.HttpContext.Connection.RemoteIpAddress);
return Task.CompletedTask;
}
};
这些日志在排查“谁在什么时候用什么Token访问了什么接口”时,价值远超任何监控图表。它不增加业务逻辑,却让安全审计变得触手可及。
简介:一套即拿即用的ASP.NET Core Web API项目,专注解决前后端分离场景下的用户身份认证问题。内置完整的JWT流程:从用户登录后生成Token(支持自定义载荷、签名密钥、过期时间),到API请求时自动校验Bearer Token有效性、处理过期与无效签名,再到解析角色声明实现接口级权限控制。所有JWT相关配置(如Issuer、Audience、密钥、有效期)统一收口在appsettings.中,适配开发/生产环境切换。项目不含Redis等外部依赖,纯内存验证,真正无状态;控制器示例(TestController、WeatherForecastController)已预置[Authorize]特性,开箱即可测试登录态拦截效果。目录结构标准清晰,包含Model层用户模型(JwtUser)、配置封装(JwtConfig)、Token生成工具(GenerateJwt)、Startup或Program启动配置,兼容.NET 6及以上版本,可直接作为新项目的认证模块参考,或快速集成进现有Web API工程。
&spm=1001.2101.3001.5002&articleId=162256384&d=1&t=3&u=46e2fdd35f7b433aa4c38a6da7722f3f)
370

被折叠的 条评论
为什么被折叠?



