news 2026/9/14 14:02:04

.NET6 WebApi 接口 JWT 鉴权实现与测试源码解析

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
.NET6 WebApi 接口 JWT 鉴权实现与测试源码解析

简介:.NET6平台下的WebApi开发中,使用JWT进行用户鉴权是一项关键实践。该资源面向具有一定C#基础、希望快速掌握JWT认证与Swagger联调的.NET开发者,提供了一套可直接运行的测试工程,完整演示从用户登录获取令牌,到携带Authorization头访问受保护接口的全过程。包体内共68个文件,以dll、cs、json等为主,并包含sln解决方案与csproj工程文件,便于直接打开编译运行;整体仅1.43MB,轻量实用。项目按AuthenticationModel、AuthenticationService、AuthenticationOperation等层次划分,清晰展示数据模型、业务逻辑与操作入口的职责边界;同时预置Swagger UI,通过AddSwaggerGen配置JWT认证选项,帮助开发者直观调试接口并验证[Authorize]特性效果。目前已有4502人学习下载,适合需要快速搭建安全WebApi或理解JWT鉴权机制的中初级.NET开发者,可作为二次开发与权限扩展的基础模板。

1. 为什么 .NET6 WebApi 接口要自己做 JWT 鉴权

你拿到一个 .NET6 WebApi 项目,接口能跑,但全部裸奔:用 Postman 改一下请求里的用户 ID,就能看到别人的订单数据,这种项目上线就是事故。JWT 是这一类无状态接口最常见的用户鉴权方案:登录时签发一个 Token,后续请求带上它,服务端验签后认可身份,不需要 Session、不需要粘性会话,多个接口实例之间天然互通。它适合从零搭 WebApi 权限体系、把老项目从 Session 登录改造成 Token 登录,以及需要在不同服务之间传递用户身份的团队。标题里的"测试源码"不是某份开源包,而是鉴权链路本身要有一组可复现的验证代码:登录脚本、带 Token 请求、状态码断言,没有它们你无法证明接口真的被保护住了。下面的内容从原理讲到实现,再把测试方法和续签、排错一次性说清楚。

2. JWT 鉴权原理:Token 结构、token 和 jwt 的区别与 .NET6 中间件选型

2.1 JWT 三段结构里,哪一段是给后端看的

JWT 是一串用点号切开的三段字符:Header.Payload.Signature。Header 是开头那一段 JSON,写签名算法和类型,通常是{"alg":"HS256","typ":"JWT"}。Payload 是中间段,JSON 里放 claims,也就是你想让后端信任的用户属性,比如用户 ID、角色、部门。Signature 是最后一段,用约定好的密钥把前两段内容做哈希计算,保证任何一处改动都能在验签时被发现。

一个要先说透的点:JWT 是签名不是加密。三段内容全部是 Base64Url 编码,任何拿到 Token 的人不需要密钥就能把 Payload 解出来读。所以密码、身份证号、手机号这类敏感数据不要放进 Payload,放进去了等于明文贴在请求头上。对 .NET6 后端来说,真正可信的数据是验签通过后的那份 claims;没通过验签的 Token 在中间件层面就被拦掉,控制器里拿到的 User 对象一定来自已验证的内容。

2.2 token 和 jwt 的区别:为什么别把两者划等号

"token 和 jwt 的区别"是 JWT 类接口下检索量不低的关键词,很多人把 Token 直接当成 JWT 来理解。Token 是个宽泛称呼:Session ID、随机字符串、OAuth access token 都叫 Token;JWT 只是 Token 里的一种结构化格式,把信任信息编码进字符串本身。传统随机 Token 不透明,服务端必须查 Session 或 Redis 才知道它代表谁;JWT 自包含,验签通过后直接从 Payload 读出身份,不用查存储。

这个区别直接决定 .NET6 里的选型。接口要横向扩容、多个实例共享流量时,JWT 能省掉集中式会话存储这一层;如果你明确需要"踢人下线、让某个 Token 立刻失效",JWT 天生不擅长,签发出去的服务端没有状态可查,除非自己维护黑名单。先问业务场景接不接受无状态,再决定要不要用 JWT,而不是看别人都在用就跟着上。

2.3 .NET6 里 JwtBearer 与 ASP.NET Core Identity 的职责分工

另一个高频困惑是"用了 JWT 还要不要搭 Identity"。两者管的不是同一层:ASP.NET Core Identity 负责用户存储、密码哈希、角色管理、登录锁定这些账号体系能力;JwtBearer 中间件只负责把 Authorization 请求头里的 Bearer Token 解析出来并验签,再把合法身份放进 HttpContext.User。Identity 不是 JWT 的前置条件,可以只有一张用户表加密码哈希就签发 JWT;接入 Identity 后把用户 ID 和角色写进 Claim 也可以。

方案身份信息存在哪多实例部署主动撤销.NET6 对应组件
Session服务端内存或共享存储需共享存储或粘性会话删 Session 即可内置 Session 中间件
随机 Token服务端或缓存需查存储删记录即可自定义过滤器
JWTToken 本身无需共享状态需额外黑名单JwtBearer

.NET6 里中间件注册顺序有硬性要求:UseAuthentication 必须在 UseAuthorization 之前,且都在 MapControllers 之前。顺序写反的典型症状是接口加了 [Authorize] 却一路放行,或者全部 401,查业务日志找不出任何原因:

var app = builder.Build(); app.UseAuthentication(); // 先认证:把请求头里的 JWT 解析成 User app.UseAuthorization(); // 后授权:判断 User 是否满足策略 app.MapControllers(); app.Run();

顺序说明:认证阶段解决"你是谁",授权阶段解决"你配不配"。两个中间件一前一后串起来,少注册任何一个,[Authorize] 都不会按预期工作。这块先立住,后面所有的排错都会回到这个顺序上。

3. .NET6 WebApi 启用 JWT:配置项、中间件注册与最小可运行代码

3.1 创建项目并安装 JwtBearer 包

最小工程用命令行一把建完:

dotnet new webapi -n JwtDemo cd JwtDemo dotnet add package Microsoft.AspNetCore.Authentication.JwtBearer

第一条命令生成 .NET6 的 WebApi 模板,第二条进入项目,第三条把认证中间件包拉进来。JwtBearer 依赖 Authentication.Core,会顺带引入 IdentityModel.Tokens.Jwt,后者负责 JWT 的生成与解析,不需要单独再加装。模板自带的 WeatherForecast 控制器只是为了验证脚手架能跑,做鉴权演示时建议删掉,避免测试请求误打到示例接口上,干扰状态码判断。

3.2 appsettings.json 的 JWT 配置:Issuer、Audience、Key 怎么填

接着在 appsettings.json 加一节独立的 JWT 配置:

{ "Jwt": { "Issuer": "JwtDemo.Api", "Audience": "JwtDemo.Client", "SecretKey": "JwtDemo-Dev-Key-ChangeMe-At-Least-32Bytes", "ExpiresMinutes": 30 } }

参数含义:Issuer 是签发方标识,Audience 是合法的调用方标识,验签时 Token 里的issaud和这两个值对不上就判定无效。SecretKey 是 HMAC 对称签名密钥,开发环境可以用上面的占位字符串,但准备发布 webapi 项目时一定要换成随机生成的 32 字节以上密钥,并放到环境变量里。.NET6 配置系统支持Jwt__SecretKey双下划线写法,它自动映射到Jwt:SecretKey节点,这样生产环境的 appsettings.json 里可以不出现明文密钥,代码一行不用改。

3.3 Program.cs 里注册认证与授权

Program.cs 是 .NET6 时代的入口,最小可运行版本如下:

using System.Text; using Microsoft.AspNetCore.Authentication.JwtBearer; using Microsoft.IdentityModel.Tokens; var builder = WebApplication.CreateBuilder(args); builder.Services.AddControllers(); var jwtSection = builder.Configuration.GetSection("Jwt"); builder.Services.AddAuthentication(JwtBearerDefaults.AuthenticationScheme) .AddJwtBearer(options => { options.TokenValidationParameters = new TokenValidationParameters { ValidateIssuer = true, ValidIssuer = jwtSection["Issuer"], ValidateAudience = true, ValidAudience = jwtSection["Audience"], ValidateIssuerSigningKey = true, IssuerSigningKey = new SymmetricSecurityKey( Encoding.UTF8.GetBytes(jwtSection["SecretKey"])), ValidateLifetime = true, ClockSkew = TimeSpan.FromMinutes(1) }; }); builder.Services.AddAuthorization(); var app = builder.Build(); app.UseAuthentication(); app.UseAuthorization(); app.MapControllers(); app.Run();

逻辑说明:AddAuthentication 声明默认认证方案为 JwtBearer,控制器上的 [Authorize] 不指定方案时走这里。AddJwtBearer 里的 TokenValidationParameters 是整套鉴权规则的核心,决定哪些字段被校验、密钥是什么、过期容差多少。最后三段管道注册顺序错一处,鉴权效果就完全不对。各配置项管辖的边界整理成下表:

参数作用常见误配
ValidateIssuer校验 Token 的iss是否等于 ValidIssuer开了校验却忘记填 ValidIssuer
ValidateAudience校验aud是否匹配调用方多个服务共用一个 aud
ValidateIssuerSigningKey用配置的密钥验签设为 false 等于没鉴权
ValidateLifetime校验exp过期时间漏设,过期 Token 照样通行
ClockSkew时钟偏移容差留在默认 5 分钟不做调整

3.4 两个容易翻车的参数边界:ClockSkew 与密钥长度

3.4.1 ClockSkew 不该用默认值

ClockSkew 是给服务器时钟不同步留的缓冲,默认是 5 分钟。意思是 Token 过期后最多还能再用 5 分钟,不少生产事故的根因就是这个默认值:你以为 Token 已经失效,灰度环境却还在放行。内部系统直接把 ClockSkew 压到 30 秒到 1 分钟,外部系统留 2 分钟足够,不要放任默认值。

3.4.2 HMAC 密钥长度不足的隐性问题

SymmetricSecurityKey 对 HS256 有最小长度要求:256 位,也就是 32 字节。密钥写短了,AddJwtBearer 注册时不一定报错,真实请求进来验签才会抛异常,表现为登录成功但访问接口一直 500。生成密钥用RandomNumberGenerator.GetBytes(32)取随机字节再转 Base64,比手敲一串看得过去的字符串可靠得多。

注意: 配置项里的 Validate 系列开关少一个,JWT 鉴权就是不完整的。最典型的翻车写法是 ValidateIssuerSigningKey = false,等于把 JWT 当透明字符串用,任何人都能伪造身份。

4. 用户鉴权核心实现:登录签发 Token 与接口级授权策略

4.1 登录接口:校验用户后生成 JWT

用户鉴权的最小闭环是:登录接口收到用户名密码,校验通过后签发 Token;后续请求带 Token 访问受保护接口。下面是 AuthController 的完整写法:

using System.IdentityModel.Tokens.Jwt; using System.Security.Claims; using System.Text; using Microsoft.AspNetCore.Authorization; using Microsoft.AspNetCore.Mvc; using Microsoft.IdentityModel.Tokens; [ApiController] [Route("api/[controller]")] public class AuthController : ControllerBase { private readonly IConfiguration _config; public AuthController(IConfiguration config) { _config = config; } [HttpPost("login")] [AllowAnonymous] public IActionResult Login(LoginRequest request) { // 生产环境换成查用户表 + 密码哈希比对 if (request.UserName != "admin" || request.Password != "123456") { return Unauthorized(new { message = "用户名或密码错误" }); } var claims = new List<Claim> { new Claim(ClaimTypes.Name, request.UserName), new Claim(ClaimTypes.NameIdentifier, "10001"), new Claim(ClaimTypes.Role, "admin"), new Claim("dept", "rd") }; var jwtSection = _config.GetSection("Jwt"); var securityKey = new SymmetricSecurityKey( Encoding.UTF8.GetBytes(jwtSection["SecretKey"])); var credentials = new SigningCredentials( securityKey, SecurityAlgorithms.HmacSha256); var expiredAt = DateTime.Now.AddMinutes( Convert.ToDouble(jwtSection["ExpiresMinutes"])); var token = new JwtSecurityToken( issuer: jwtSection["Issuer"], audience: jwtSection["Audience"], claims: claims, notBefore: DateTime.Now, expires: expiredAt, signingCredentials: credentials); return Ok(new { token = new JwtSecurityTokenHandler().WriteToken(token), expires = expiredAt }); } } public record LoginRequest(string UserName, string Password);

参数说明:JwtSecurityToken 的 expires 决定 Token 有效时长,notBefore 指生效时间,取当前时间即可。WriteToken 把对象转成三段字符串,这是唯一需要返回给客户端的凭据。登录失败统一返回 401 且不区分"用户不存在"和"密码错误",防止账号枚举。示例里是明文比对,真实的密码校验至少用 BCrypt 或 PBKDF2 哈希,不做哈希的密码存储属于安全事故。

4.2 把角色和用户 ID 放进 Claim,并在接口里取出来

Claim 是 JWT 里描述用户属性的键值对。实际项目最少放用户主键、登录名、角色,多租户系统再加租户 ID。角色字段建议用 ClaimTypes.Role,这样可以直接配 [Authorize(Roles = "admin")] 的写法,不用自己写判断。取值从控制器的 User 属性拿,不要去重新解析 Token:

[ApiController] [Route("api/[controller]")] [Authorize] public class OrderController : ControllerBase { [HttpGet] public IActionResult Get() { var userId = User.FindFirst(ClaimTypes.NameIdentifier)?.Value; var role = User.FindFirst(ClaimTypes.Role)?.Value; var dept = User.FindFirst("dept")?.Value; return Ok(new { userId, role, dept, message = "已通过 JWT 鉴权,按用户身份返回数据" }); } [Authorize(Roles = "admin")] [HttpDelete("{id:int}")] public IActionResult Delete(int id) { return Ok(new { deleted = id }); } }

逻辑说明:控制器上的 [Authorize] 拦截未认证请求,方法级的 [Authorize(Roles = "admin")] 在认证通过后继续检查角色,不满足返回 403。User 属性是中间件验签后写入的 claims,不要再自己解 Header。这也是把信息编码进 JWT 的好处,接口层不需要查库就能拿到用户身份。

4.2.1 取不到角色时的第一排查点

如果登录时放了 ClaimTypes.Role,接口里 User.FindFirst(ClaimTypes.Role) 却返回 null,先检查 MapInboundClaims。.NET6 默认会把入站 claim 名做映射,短 claim 名(role、sub)被转成 URI 长名。解决办法是在 AddJwtBearer 里设置options.MapInboundClaims = false;,同时在 TokenValidationParameters 里指定RoleClaimType = "role"NameClaimType = "name",角色和用户名的取用才会一致。这个坑表现为"Swagger 调试正常,换到 .NET 客户端就取不到",排查成本不低,配置时一并处理掉最省事。

4.3 自定义授权策略:接口权限不只有角色一种

角色满足不了所有场景,比如"研发部门的管理员只能操作本部门数据"。常见做法是自定义策略加 Handler:

public class DeptRequirement : IAuthorizationRequirement { } public class DeptHandler : AuthorizationHandler<DeptRequirement> { protected override Task HandleRequirementAsync( AuthorizationHandlerContext context, DeptRequirement requirement) { var dept = context.User.FindFirst("dept")?.Value; if (dept == "rd") { context.Succeed(requirement); } return Task.CompletedTask; } } // Program.cs 注册 builder.Services.AddAuthorization(options => { options.AddPolicy("DeptPolicy", policy => policy.Requirements.Add(new DeptRequirement())); }); builder.Services.AddSingleton<IAuthorizationHandler, DeptHandler>();

逻辑说明:Handler 里的 context.User 是验签后的 ClaimsPrincipal,只从 claims 判断,不要在这里查数据库,保持鉴权过程无状态。没有调用 Succeed 时请求默认 403,所以部门判断条件要写完整,漏掉 else 分支不会报错但会静默拒绝。策略适合多接口复用的权限规则;单接口的简单校验用 [Authorize(Roles = ...)] 已经足够,过度设计反而难维护。

4.4 401 和 403 的排查边界

状态码含义排查方向
401未认证请求头是否带 Bearer,Token 是否过期
403已认证但无权限角色名是否一致,策略 Handler 是否 Succeed
200 但 User 里取不到值认证过了,claim 映射不对MapInboundClaims、RoleClaimType 配置

看到 403 先对比角色字符串,JWT 角色名大小写敏感,admin 和 "Admin" 是两个值。看到 401 先抓请求头看 Authorization 是不是Bearer加三段式 Token,格式不对中间件直接拒收。这套排查表可以直接写进测试用例的断言里,比人肉盯着 Swagger 翻高效得多。

5. 测试源码组织:Swagger、curl 与 winform 客户端测 WebApi 接口

5.1 Swagger 里配置 Authorization,先跑通最小流程

.NET6 的 WebApi 模板自带 Swagger,但默认不带鉴权按钮。需要在 AddSwaggerGen 里注册 Bearer 方案:

using Microsoft.OpenApi.Models; builder.Services.AddSwaggerGen(options => { var scheme = new OpenApiSecurityScheme { Name = "Authorization", Type = SecuritySchemeType.Http, Scheme = "bearer", BearerFormat = "JWT", In = ParameterLocation.Header }; options.AddSecurityDefinition("Bearer", scheme); options.AddSecurityRequirement(new OpenApiSecurityRequirement { { new OpenApiSecurityScheme { Reference = new OpenApiReference { Type = ReferenceType.SecurityScheme, Id = "Bearer" } }, Array.Empty<string>() } }); });

逻辑说明:AddSecurityDefinition 告诉 Swagger UI 在页面上渲染一个 Authorize 按钮;AddSecurityRequirement 把该方案挂到每个接口上,请求时自动带 Authorization 头。Scheme 写 "bearer" 小写,Swagger 会正确拼成Bearer <token>。调试顺序固定为:先调用 login 接口拿 Token,点 Authorize 粘贴进去,再调业务接口;顺序反过来的话业务接口永远 401。

5.2 发布后的接口用 curl 和 Postman 验证

本地跑通不代表发布 webapi 项目之后还正常,发布环境的配置可能被环境变量改写,密钥可能被替换。最省事的线上验证是一组 curl:

# 1. 登录拿 token curl -X POST "https://api.example.com/api/Auth/login" \ -H "Content-Type: application/json" \ -d '{"userName":"admin","password":"123456"}' # 2. 带 token 调用受保护接口 curl -X GET "https://api.example.com/api/Order" \ -H "Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9..."

参数说明:第二步把第一步返回的 Token 原样粘到 Bearer 后面。生产环境不要用示例密码,登录接口必须加失败次数限制,否则等于把爆破入口敞开着。Postman 里可以把 Token 存成集合变量,登录脚本里写pm.collectionVariables.set("token", json.token),请求头引用Authorization: Bearer {{token}},后续接口全部自动带上,整套测试省掉反复复制粘贴。

5.3 winform 中测试 webapi 接口的 HttpClient 写法

内部工具经常用 winform 直接测 WebApi:窗口输入账号密码,登录后调用受保护接口展示结果。这类客户端的关键是 HttpClient 只建一个,登录成功后把 Token 写进默认请求头:

public partial class MainForm : Form { private static readonly HttpClient http = new HttpClient { BaseAddress = new Uri("https://api.example.com") }; private async void btnLogin_Click(object sender, EventArgs e) { var login = new { userName = "admin", password = "123456" }; var response = await http.PostAsJsonAsync("api/Auth/login", login); var result = await response.Content .ReadFromJsonAsync<LoginResult>(); http.DefaultRequestHeaders.Authorization = new System.Net.Http.Headers.AuthenticationHeaderValue( "Bearer", result.token); MessageBox.Show("登录成功,有效期至 " + result.expires); } private async void btnCallApi_Click(object sender, EventArgs e) { using var resp = await http.GetAsync("api/Order"); var body = await resp.Content.ReadAsStringAsync(); txtResult.Text = $"{(int)resp.StatusCode}\r\n{body}"; } } public class LoginResult { public string token { get; set; } public DateTime expires { get; set; } }

逻辑说明:PostAsJsonAsync 把匿名对象序列化后发给 login 接口,ReadFromJsonAsync 反序列化返回体拿到 Token。DefaultRequestHeaders.Authorization 是全局的,winform 单用户场景合适;多用户模拟就得每个用户建独立 HttpClient,否则后登录的人会把前一个人的 Token 顶掉。按钮事件用 async/await 而不是 .Result,界面才不会在请求期间冻结。

5.4 测试源码的目录结构与断言建议

测试源码建议独立建项目,不塞进主工程。目录按被测对象划分:

tests/JwtDemo.Api.Tests/ AuthControllerTests.cs # 登录成功、密码错误、缺 Token OrderControllerTests.cs # 有 Token 200、无 Token 401、非 admin 403 TestAuthHandler.cs # 生成合法 Token 的辅助类

TestAuthHandler 里放一个静态方法,用与 Program.cs 完全相同的配置签发 Token,测试用例直接复用,不重复写构造逻辑。断言不要只看状态码,下面几个场景要写进用例里:

测试场景预期结果断言重点
不带 Token 请求业务接口401状态码与 WWW-Authenticate 头
带过期 Token 请求401状态码,且不进入控制器
普通角色调用 admin 接口403状态码,业务数据未返回
合法 admin 调用200返回体中的用户 ID 与 Token 一致

提示: 测试工程和生产配置的 TokenValidationParameters 要完全一致,否则会出现测试全过、发布后全部 401 的经典事故。

6. 进阶:token 续签与 JWT 漏洞总结及防护清单

6.1 token 续签的两种做法:滑动过期与刷新令牌

JWT 的 expires 一到就失效,客户端被迫重新登录。内部系统我优先用滑动过期:业务接口收到合法 Token 后判断剩余有效期,低于阈值就签发一个全新 Token 随响应返回,前端收到后替换本地存储。代价是控制器里多一次时间比较,适合用户长期在线的后台管理系统。

6.1.1 刷新令牌的落地结构

需要严格控制权限的系统用 refresh_token 方案:登录时发一对,access_token 短效,refresh_token 长效 7 天。刷新令牌存进数据库并记录用户、设备、签发时间。关键在于续签接口——校验旧刷新令牌有效后立即吊销它,返回全新的一对:

[HttpPost("refresh")] public IActionResult Refresh(RefreshRequest request) { // 校验 refresh_token 是否有效且未吊销 var valid = _tokenStore.FindValid(request.RefreshToken, out var userId); if (!valid) { return Unauthorized(new { message = "刷新令牌无效或已过期" }); } // 吊销旧令牌,防止重放 _tokenStore.Revoke(request.RefreshToken); var newAccessToken = BuildAccessToken(userId); var newRefreshToken = Guid.NewGuid().ToString("N"); _tokenStore.Save(newRefreshToken, userId, DateTime.UtcNow.AddDays(7)); return Ok(new { accessToken = newAccessToken, refreshToken = newRefreshToken }); }

参数说明:refresh_token 用不透明随机串而不是 JWT,因为服务端要能主动吊销它。存储时间统一用 UTC,避免服务器与客户端时区差导致续签提前失效。refresh_token 泄露风险高于 access_token,续签接口必须走 HTTPS,这是底线。

6.2 jwt 漏洞总结:算法混淆、密钥过弱与校验项缺失

JWT 相关的漏洞大多不在 .NET6 框架本身,而在配置和用法上。检索"jwt 漏洞总结"看到的高频案例集中在以下几类:

漏洞成因防护手段
alg=none 攻击服务端接受无签名 Token显式指定算法,如 HmacSha256
RS256 置换 HS256用公钥当 HMAC 密钥验签算法白名单,区分对称与非对称密钥
密钥过弱或硬编码弱密钥可被离线爆破32 字节以上随机密钥,密钥走环境变量
校验项缺失ValidateLifetime 未开按 3.3 的配置逐项核对

.NET6 里固定算法的方式是签发与验签两侧都写死 SecurityAlgorithms.HmacSha256,不要依赖默认推断。更严格的团队会用自定义 IssuerSigningKeyResolver 做算法白名单,把允许的算法数组放进配置,白名单之外的 alg 一律拒绝。想系统练手可以跑 OWASP WebGoat 的 JWT 关卡,它把 alg=none、密钥爆破、算法置换逐个场景化,做完再回来看自己的配置,印象会深很多。

6.3 加固后的自测清单

按顺序过一遍,全部满足才算这套 JWT 用户鉴权真正立住:

  • 不带 Token 访问受保护接口,确认 401
  • 用错误密钥签一个 Token,确认 401
  • 修改合法 Token Payload 里的角色字段,确认 401 而不是 403
  • 等 Token 过期后再请求,确认 401 且响应没有明显变慢
  • winform 客户端登录后连续调用接口,确认每次请求都带 Authorization 头
  • 用 5.2 的 curl 在发布 webapi 项目的服务器上重跑一遍,确认环境变量没有改掉密钥

最后一条最容易翻车:开发用密钥 A、生产用密钥 B 切换时,老 Token 全部失效,用户被迫全体重新登录。发布前先在测试环境完成密钥轮换并跑一遍上述清单,确认老的 access_token 即时失效、刷新令牌能续上,再去动生产配置。

本文还有配套的精品资源,点击获取

版权声明: 本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若内容造成侵权/违法违规/事实不符,请联系邮箱:809451989@qq.com进行投诉反馈,一经查实,立即删除!
网站建设 2026/9/14 14:00:33

Dubins与Reeds-Shepp路径规划:自动驾驶中满足运动学约束的最短解析解

简介&#xff1a;本资源是面向机器人路径规划与自动驾驶算法研究者的Reeds-Shepp曲线Matlab实现工具包&#xff0c;聚焦于满足车辆最小转弯半径与正/反向行驶约束的最优路径建模问题。资源仅含1个核心文件——dubin.m&#xff0c;为Matlab环境下可直接调用的函数脚本&#xff0…

作者头像 李华
网站建设 2026/9/14 14:00:22

文本标注工具选型与工业级工作流搭建指南

简介&#xff1a;这是一套面向人工智能初学者与NLP项目实践者的文本标注工具实战资源&#xff0c;聚焦文本分类任务中的关键环节——人工打标签与语义要素提取。资源提供开箱即用的Python桌面标注工具&#xff0c;支持为单条文本批量添加多个类别标签&#xff0c;并自动识别并抽…

作者头像 李华
网站建设 2026/9/14 14:00:22

智能体技术如何重塑大学生就业市场

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

作者头像 李华
网站建设 2026/9/14 13:59:45

SpringBoot+Vue校园招聘管理系统:表设计、权限控制与答辩验证

简介&#xff1a;基于Spring Boot与Vue的校园招聘管理系统&#xff0c;是一份答辩通过的高分毕业设计源码项目&#xff0c;适合Java方向的毕业生或在校学生用于毕业设计、课程设计、期末大作业等场景。系统覆盖企业、职位、简历投递与后台管理等校园招聘核心功能&#xff0c;能…

作者头像 李华
网站建设 2026/9/14 13:59:44

Arnis:30 分钟在 Minecraft 里复刻一座真实城市,免费开源

Arnis&#xff1a;30 分钟在 Minecraft 里复刻一座真实城市&#xff0c;免费开源 【免费下载链接】arnis Generate any location from the real world in Minecraft with a high level of detail. 项目地址: https://gitcode.com/GitHub_Trending/ar/arnis 框出老家所在…

作者头像 李华
网站建设 2026/9/14 13:59:36

MyBatisPlus插件机制解析与实战应用

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

作者头像 李华