.NET6 WebApi 用户鉴权实战:JWT 登录与令牌校验完整链路
简介这是一份基于.NET6平台构建WebApi并集成JWT用户鉴权与Swagger测试的完整示例源码面向正在学习C# WebApi开发、需要落地前后端分离鉴权流程的中级开发者。资源将用户登录、令牌生成与验证、接口授权及Swagger联调整合在同一个解决方案中可以直接运行并对照理解。压缩包共68个文件以dll、json、cs、cache等类型为主其中cs源码对应控制器、服务与模型分层json含配置文件与依赖信息dll为项目构建所引用的程序集整体大小仅1.43MB方便快速下载与部署。目前已有4503人学习或下载。通过这套工程读者能掌握JwtSecurityTokenHandler生成令牌、在Authorization头传递JWT、配合[Authorize]保护接口以及为Swagger配置JWT鉴权按钮等关键技能可作为团队开发或毕业设计中的用户权限基础模块也可继续扩展角色管理、刷新令牌等功能。1. 从登录接口到JWT鉴权这份.NET6 WebApi资源解决了什么问题联调第一天前端把token放Authorization头里发过来后端接口却直接401——不是token过期而是整条JWT校验管道没接上签发的token没人验。这个现象在新手项目里出现的频率高得吓人。这份源码包给出的正是.NET6平台上一整套能直接跑起来的WebApi用户鉴权方案三层项目结构Model / Operation / Service、登录接口颁发JWT、Swagger里一键携带token调试受保护接口。和网上只贴两段代码的教程不同它把Controller、业务操作类、模型、配置全部分层放好适合刚接触C# WebApi、想弄懂用户鉴权完整链路的新手也适合想给没做鉴权的老项目快速接入JWT的开发者。2. 拆解AuthenticationService.sln三层项目结构与JWT运行链路打开源码包先看AuthenticationService.sln。一个解决方案文件对应三个项目AuthenticationModel、AuthenticationOperation、AuthenticationService。这也是很多.NET服务端项目的常见分层数据模型放一层、业务操作放一层、Web宿主放一层。这种拆分不是摆设它解决的是复用和职责边界的问题——同一个token生成逻辑放到Operation类库里以后再做管理后台、再造一个调用端直接引用类库就可以不需要把Controller那一层也搬过去。2.1 三个项目各管什么Model、Operation、Service的职责边界文件列表里能明显看到的几个关键入口AuthenticationService下有Controllers、Program.cs、appsettings.jsonAuthenticationOperation下有AuthenticationOperation.cs、OperationClass、UtilityAuthenticationModel下有AuthenticationModel.cs。按我拆项目的习惯先看Controllers和Program.cs因为它们是入口再看AuthenticationOperation里的JwtTokenOperation这是整个鉴权的核心逻辑最后补AuthenticationModel里的实体定义。AuthenticationService.sln ├── AuthenticationService/ // WebApi宿主接收HTTP请求 │ ├── Controllers/ │ │ ├── AuthenticationController.cs │ │ └── WeatherForecastController.cs │ ├── Program.cs │ └── appsettings.json ├── AuthenticationOperation/ // 业务操作类生成Token的核心逻辑 │ ├── AuthenticationOperation.cs │ ├── OperationClass/ │ └── Utility/ └── AuthenticationModel/ // 数据模型身份验证相关实体 └── AuthenticationModel.cs这个目录结构对应的依赖关系是AuthenticationService引用AuthenticationOperationAuthenticationOperation引用AuthenticationModel。所以Controller里只写HTTP请求相关的动作token生成细节在Operation层实体定义回Model。新手最容易犯的错是把UserInfo模型直接写在Controller文件里短期看着方便一旦要加角色管理、权限扩展就到处找类。分成三层之后扩展点非常清晰加字段去Model加逻辑去Operation加接口去Controller。有一点要提前说明AuthenticationOperation类库要读到appsettings.json里的JwtSettings需要引用Microsoft.Extensions.Configuration.Abstractions这个包。源码包编译时如果提示缺依赖在类库上右键“管理NuGet程序包”装一下就好不需要改任何代码。2.2 JWT令牌的三段结构Header、Payload、SignatureJWT不是一串无意义的乱码由Header、Payload、Signature三段Base64Url编码拼接而成各段用点号隔开。一个典型的JWT长这样eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9 . eyJodHRwOi8vc2NoZW1hcy54bWxzb2FwLm9yZy93cy8yMDA1LzA1L2lkZW50aXR5L2NsYWltcy9uYW1lIjoiYWRtaW4iLCJleHAiOjE3MzAwMDAwMDAsImlhdCI6MTczMDAwMDAwMH0 . xgEYdHgfH8bPcT9pY7yIzCzJ6sTMO3zY_KXjD9TJG4s第一段Header声明了加密算法和令牌类型这里alg是HS256typ是JWT。第二段Payload存放claims也就是登录用户是谁、角色是什么、过期时间exp、签发时间iat这些信息。第三段Signature是签名做法是拿前两段拼接的字符串用HMACSHA256和密钥算出来的哈希值。签名是最关键的部分。Header和Payload是Base64Url编码不是加密任何人都能解码看内容但第三段签名只有持有密钥的服务端能生成和校验。客户端如果偷偷把用户名从admin改成guest签名会立刻失效因为重新算出的哈希对不上。这个机制决定了JWT适合做无状态鉴权服务端不存session拿到token验一下签名就够了。2.3 完整请求链路登录、携带、校验把整条链路串起来看是这样一个过程客户端先调登录接口服务端校验账号密码后生成JWT返回客户端把token存到localStorage或内存里后续每次请求在Header带Authorization: Bearer {token}。服务端由认证中间件解析并校验签名、过期时间、签发者等信息校验通过就把token里的claims转成当前用户身份[Authorize]接口才放行。这套流程在AuthenticationService里对应三段代码JwtTokenOperation类负责生成tokenProgram.cs里AddJwtBearer负责配置校验参数AuthenticationController负责登录入口和受保护接口。我习惯把这个链路理解成两次握手的组合第一次是账号密码换token第二次是token换用户身份。第一握手里token要不要包含角色信息直接决定了第二握手时接口能不能区分普通用户和管理员。比如后面要做的[Authorize(Roles Admin)]这类限制依赖的就是签发token时把角色写进了claims。3. 登录接口与Token生成把核心代码跑通原理放在前面是为了让代码有落脚的依据。接下来直接动手从建项目到调通登录接口按我平时搭WebApi的顺序一步步来。整套源码的核心代码集中在三处appsettings.json的JwtSettings节点、Program.cs里的认证注册、AuthenticationOperation里的JwtTokenOperation。先把这三处写清楚登录接口就顺理成章了。3.1 安装NuGet包与配置appsettings.json要在.NET6 WebApi里跑通JWT需要先安装四个包System.IdentityModel.Tokens.Jwt、Microsoft.IdentityModel.Tokens、Microsoft.AspNetCore.Authentication.JwtBearer、Swashbuckle.AspNetCore。前两个负责生成和解析JWT第三个是认证中间件第四个是Swagger文档。源码包里已经处理好了包引用这里列出是为了让照着手搭项目的读者知道依赖从哪来。{ JwtSettings: { Issuer: AuthenticationService, Audience: AuthenticationApiClient, SecretKey: YourSuperSecretKeyForJwtSigningAtLeast32BytesLongValue, ExpiresMinutes: 120 }, Logging: { LogLevel: { Default: Information, Microsoft.AspNetCore: Warning } }, AllowedHosts: * }JwtSettings这四个参数是整套鉴权的基础配置Issuer表示签发者Audience表示接受方SecretKey是签名密钥ExpiresMinutes控制token有效期。实际项目中我会把SecretKey放到用户机密或环境变量里避免明文提交到仓库。密钥还有一个硬性要求HS256签名算法要求SecretKey换算成字节后不少于32字节所以字符串长度最好控制在32个字符以上并且用随机字符串不要用“123456”这种。参数示例值作用注意点IssuerAuthenticationService签发者标识与TokenValidationParameters.ValidIssuer保持一致AudienceAuthenticationApiClient受众标识与ValidAudience保持一致SecretKey32字节以上随机字符串签名密钥密钥泄露等于任何人都能伪造tokenExpiresMinutes120token过期时间真实项目建议15-30分钟并配合刷新3.2 Program.cs注册认证服务与TokenValidationParametersProgram.cs是.NET6 WebApi程序的入口认证服务的注册和中间件的挂载都发生在这里。这段配置如果你照着网上的老教程抄很容易漏掉AddAuthentication那一段直接写AddJwtBearer结果就是后面调接口时认证方案没有默认值。using System.Text; using Microsoft.AspNetCore.Authentication.JwtBearer; using Microsoft.IdentityModel.Tokens; using AuthenticationOperation; var builder WebApplication.CreateBuilder(args); var jwtSettings builder.Configuration.GetSection(JwtSettings); builder.Services.AddScopedJwtTokenOperation(); builder.Services.AddAuthentication(options { options.DefaultAuthenticateScheme JwtBearerDefaults.AuthenticationScheme; options.DefaultChallengeScheme JwtBearerDefaults.AuthenticationScheme; }) .AddJwtBearer(options { options.TokenValidationParameters new TokenValidationParameters { ValidateIssuer true, ValidIssuer jwtSettings[Issuer], ValidateAudience true, ValidAudience jwtSettings[Audience], ValidateIssuerSigningKey true, IssuerSigningKey new SymmetricSecurityKey( Encoding.UTF8.GetBytes(jwtSettings[SecretKey]) ), ValidateLifetime true, ClockSkew TimeSpan.FromMinutes(1) }; }); builder.Services.AddControllers(); builder.Services.AddEndpointsApiExplorer(); var app builder.Build(); app.UseAuthentication(); app.UseAuthorization(); app.MapControllers(); app.Run();这段代码做的是三件事注册Service层依赖、配置认证方案、挂载中间件。TokenValidationParameters里的几个Validate开关分别控制是否校验签发者、受众、签名密钥和过期时间实际使用中建议全部打开。ClockSkew默认值是5分钟意思是允许token过期后5分钟内的宽容期如果你希望到点立即失效就把值设小一点。我一般设1分钟既避免客户端和服务器时间误差导致的误伤又不至于让过期token在5分钟内还能用。这里有个顺序坑提前说app.UseAuthentication()必须在app.UseAuthorization()之前调用。如果顺序写反认证中间件还没执行授权中间件拿不到用户身份[Authorize]接口会全部失守。3.3 JwtTokenOperationToken生成的核心实现JwtTokenOperation放在AuthenticationOperation层专门负责生成JWT。这个类只做一件事接收一个用户实体把用户信息塞进claims然后返回签好的token。using System.IdentityModel.Tokens.Jwt; using System.Security.Claims; using System.Text; using Microsoft.IdentityModel.Tokens; using AuthenticationModel; namespace AuthenticationOperation { public class JwtTokenOperation { private readonly IConfiguration _configuration; public JwtTokenOperation(IConfiguration configuration) { _configuration configuration; } public string GenerateToken(UserInfo user) { var secretKey _configuration[JwtSettings:SecretKey]; var issuer _configuration[JwtSettings:Issuer]; var audience _configuration[JwtSettings:Audience]; var expiresMinutes Convert.ToInt32(_configuration[JwtSettings:ExpiresMinutes]); var claims new[] { new Claim(ClaimTypes.Name, user.UserName), new Claim(ClaimTypes.NameIdentifier, user.Id.ToString()), new Claim(ClaimTypes.Role, user.Role) }; var key new SymmetricSecurityKey(Encoding.UTF8.GetBytes(secretKey)); var credentials new SigningCredentials(key, SecurityAlgorithms.HmacSha256); var token new JwtSecurityToken( issuer: issuer, audience: audience, claims: claims, notBefore: DateTime.UtcNow, expires: DateTime.UtcNow.AddMinutes(expiresMinutes), signingCredentials: credentials ); return new JwtSecurityTokenHandler().WriteToken(token); } } }生成token的过程可以拆成五步声明claims、创建对称密钥、创建签名凭据、构造JwtSecurityToken、用Handler序列化成字符串。claims里的ClaimTypes.Name、ClaimTypes.NameIdentifier、ClaimTypes.Role分别对应用户显示名、用户ID和角色这三条在后面接口鉴权时会被频繁读取。notBefore和expires我统一用DateTime.UtcNow避免本地时间与UTC时间差导致token提前过期。JwtSecurityTokenHandler().WriteToken最终把对象转成客户端看到的那个三段落字符串。3.4 AuthenticationController登录入口与用户信息读取Controller这一层只做HTTP动作登录校验逻辑在这份源码里是硬编码的admin/123456真实项目替换成查数据库即可。重点看token如何从生成到返回以及受保护接口如何用[Authorize]挡人。using Microsoft.AspNetCore.Authorization; using Microsoft.AspNetCore.Mvc; using System.Security.Claims; using AuthenticationModel; using AuthenticationOperation; namespace AuthenticationService.Controllers { [ApiController] [Route(api/[controller])] public class AuthenticationController : ControllerBase { private readonly JwtTokenOperation _tokenOperation; public AuthenticationController(JwtTokenOperation tokenOperation) { _tokenOperation tokenOperation; } [HttpPost(login)] [AllowAnonymous] public IActionResult Login([FromBody] LoginRequest request) { // 示例项目没有接数据库先写死账号密码真实项目改为查数据表 if (request.UserName admin request.Password 123456) { var user new UserInfo { Id 1, UserName request.UserName, Role Admin }; var token _tokenOperation.GenerateToken(user); return Ok(new LoginResult { Success true, Token token, Message 登录成功 }); } return Unauthorized(new LoginResult { Success false, Token , Message 用户名或密码错误 }); } [HttpGet(userinfo)] [Authorize] public IActionResult GetUserInfo() { var userId User.Claims.FirstOrDefault(c c.Type ClaimTypes.NameIdentifier)?.Value; var userName User.Identity.Name; var role User.Claims.FirstOrDefault(c c.Type ClaimTypes.Role)?.Value; return Ok(new { UserId userId, UserName userName, Role role, Message token有效接口访问成功 }); } } }[AllowAnonymous]放在Login上表示这个接口不需要token就能访问[Authorize]放在GetUserInfo上表示必须携带有效token。User.Identity和User.Claims是认证中间件解析token后填充的用户身份取出来的就是签发时写进去的claims。有一点要注意User.Identity.Name能不能取到值取决于签发时是否用了ClaimTypes.Name。如果随便写个字符串“UserName”这里Identity.Name就是空后面第5章会专门讲这个坑。4. Swagger配置与接口鉴权让调试工具直接带上token做WebApi开发Swagger基本成了标配。它不只是看接口文档的地方还能直接调试接口。但默认情况下Swagger不会自动携带token需要手动配置安全方案。如果在Swagger页面上点开接口直接执行不带token的请求必然401。所以这一步要解决两件事让Swagger显示出Authorize按钮让用户填了token之后所有调试请求都自动带上Authorization头。4.1 给Swagger挂上Authorize按钮Swagger的JWT配置依赖Swashbuckle.AspNetCore包。核心动作是在AddSwaggerGen里调用AddSecurityDefinition和AddSecurityRequirement把JWT的Bearer认证方案声明进去。builder.Services.AddSwaggerGen(options { options.SwaggerDoc(v1, new OpenApiInfo { Title AuthenticationService API, Version v1 }); options.AddSecurityDefinition(Bearer, new OpenApiSecurityScheme { Name Authorization, Type SecuritySchemeType.Http, Scheme Bearer, BearerFormat JWT, In ParameterLocation.Header, Description 请输入JWT令牌格式Bearer {token} }); options.AddSecurityRequirement(new OpenApiSecurityRequirement { { new OpenApiSecurityScheme { Reference new OpenApiReference { Type ReferenceType.SecurityScheme, Id Bearer } }, Array.Emptystring() } }); });这段配置里最关键的是SecuritySchemeType.Http加SchemeBearer的组合。它告诉Swagger这是一个HTTP Bearer认证Swagger UI会渲染成一个输入框执行调试时自动把Authorization头拼成“Bearer {你输入的内容}”。前阵子按老教程配置的人喜欢用SecuritySchemeType.ApiKey加InHeader那样Swagger虽然也能显示输入框但不会自动加Bearer前缀需要手动把Bearer三个字一起填进去容易踩第5章的坑。SecurityRequirement那段是把刚才定义好的方案挂到所有接口上如果没有这段界面右上角不会出现Authorize按钮或者出现了也不生效。Program.cs里还需要启用Swagger中间件一般放在开发环境的判断里if (app.Environment.IsDevelopment()) { app.UseSwagger(); app.UseSwaggerUI(); }正式发布WebApi项目时记得把这段包在IsDevelopment()里或者用条件编译控制别让生产环境暴露Swagger文档。这一步经常被忽略接口文档一旦开放出去等于给攻击者递了地图。4.2 [Authorize]与[AllowAnonymous]哪些接口需要token鉴权的最小单位是Controller里的Action。把[Authorize]挂在Controller类上表示整个控制器的所有接口都需要token挂在具体Action上则只保护那一个接口。对应的[AllowAnonymous]用来放行公开接口。这套源码里的AuthenticationController就是典型的组合用法整个类的接口都要求登录但Login接口用[AllowAnonymous]放行毕竟登录时还没有token。从设计角度讲我一般建议全站默认鉴权只对登录、注册、验证码这类接口开放。做法是给Controller统一加[Authorize]再在个别Action上叠加[AllowAnonymous]。如果项目里Controller很多也可以直接在Program.cs里配置FallbackPolicy实现所有没有显式标注的接口默认都要求认证。这一点是很多新手忽略的光在Swagger里看到接口列表不代表所有接口都被保护了。4.3 从Claims读取用户身份信息接口鉴权通过后业务代码里拿用户身份的方式是通过Controller基类的User属性。User的类型是ClaimsPrincipal它由认证中间件在解析token时构造出来token里写进去的claims会映射到User.Claims集合中。读起来就是遍历用户Claims的过程。[HttpGet(userinfo)] [Authorize] public IActionResult GetUserInfo() { var userId User.Claims.FirstOrDefault(c c.Type ClaimTypes.NameIdentifier)?.Value; var userName User.Identity.Name; var role User.Claims.FirstOrDefault(c c.Type ClaimTypes.Role)?.Value; return Ok(new { UserId userId, UserName userName, Role role, Message token有效接口访问成功 }); }这段代码演示了两个读取路径User.Identity.Name直接取当前登录名对应签发时的ClaimTypes.NameUser.Claims按Type取值适合取ID、角色等非姓名信息。取出来的值就是签发token时写进JWT的claim内容所以前面强调过一个项目里claims的命名必须前后一致否则这边读出来就是null。真正的线上项目里通常还会有DepartmentId、TenantId这类自定义claim读取逻辑跟这里完全一样只是Type字符串要自定义。建议把这层读取封装成一个扩展方法比如GetUserId(this ClaimsPrincipal user)避免每个Controller都写一遍FirstOrDefault。5. 常见问题排查五个绕不开的JWT实操坑这部分是我在一线对接这些项目时最常看到的踩坑记录。前面代码写得很顺一到联调就集体翻车而且翻车方式高度雷同。网上那些JWT漏洞总结第一屏基本都在讲密钥泄露、算法混淆、claims信任但落到实际项目里反而不是这些高深问题是下面五个基础动作没做到位。我把它们按“现象、原因、解决”的格式写在这里每条都能直接对照自查。5.1 Swagger弹窗里填了token接口还是401现象登录接口正常返回token在Swagger右上角Authorize里填入token并保存调userinfo接口还是401。原因安全方案定义为HttpSchemeBearer后Swagger会自动在输入框内容前拼“Bearer ”。如果你把“Bearer eyJhbGciOi...”整段粘进去实际请求头变成“Bearer Bearer eyJhbGci...”认证中间件解析失败。解决在Swagger的Authorize弹窗里只粘贴JWT本体不带Bearer前缀。粘完确认一下粘贴框里token是不是以字母e开头如果以B开头说明Bearer前缀重复了。这个坑我见的次数最多基本每次给同事调Swagger都能撞上。5.2 签名验证失败SecretKey长度与编码坑现象登录成功后拿token请求接口报IDX10503或签名不匹配签发的令牌无法通过签名校验。从JWT漏洞角度看这属于密钥配置不当导致的可用性问题虽然没有泄露但会让整个服务不可用。原因一是SecretKey长度不足HS256要求32字节用了“test123”这种太短的字符串二是appsettings.json里字符串前后带了不可见空格或者编辑器把反斜杠转义了导致生成和验证用的key不一致。解决先量一下密钥长度直接在Program.cs里临时加一行日志输出Encoding.UTF8.GetBytes(secretKey).Length确认不小于32。然后检查appsettings.json里SecretKey那一行有没有多余空格建议把字符串复制到支持显示空白字符的编辑器里看一遍。密钥生成我一般用openssl rand -base64 48一把梭取回来直接用。5.3 token刚返回就提示过期现象ExpiresMinutes配了120登录接口也返回了token但请求一次就被提示token过期或者压根走不到业务代码。原因时间基准不统一。签发时用了DateTime.Now而JWT内部统一用Unix时间戳表示UTC时间校验时按UTC解析如果你的服务器系统时区不是UTCDateTime.Now与UTC之间差出的几个小时就可能让token看起来已经失效。另一个常见原因是ClockSkew被设成TimeSpan.Zero且客户端机器时间快了几分钟严格校验就触发过期。解决所有时间字段统一用DateTime.UtcNow来设置notBefore和expires。ClockSkew不要设置成0保留1-5分钟容差。部署到海外服务器时尤其注意服务器时间的时区设置直接在服务器上执行date -u确认UTC时间是否准确。这一条在多人协作时特别容易翻车因为每个人本地时间不一样跑出来的结果也不一样最后互相甩锅。5.4 [Authorize]不生效UseAuthentication顺序问题现象给接口加了[Authorize]但不带token也能直接访问后端完全不拦。原因Program.cs里没有调用app.UseAuthentication()或者把UseAuthentication写在了UseAuthorization后面。认证中间件没执行授权中间件拿不到任何用户身份[Authorize]就不会触发401挑战。解决在app.Build()之后先UseAuthentication再UseAuthorization这是固定顺序。检查代码时直接看这两行顺序前者一旦缺失所有[Authorize]接口全部失守。这一条是影响面最大的坑线上出问题往往不是某个人改错而是几个人接手时把中间件顺序调换过。5.5 Claims读取为空类型映射不一致现象接口已经通过鉴权但User.Identity.Name为空或者Role等自定义claim取值是null。原因签发token时用了一个自定义字符串“role”当claim类型而读取时用ClaimTypes.Role或者反过来写入用ClaimTypes.Role读取用JwtSecurityTokenHandler.DefaultInboundClaimTypeMap映射后的短名导致键对不上。JWT的claim类型通常是一长串URI而JwtSecurityTokenHandler默认会把一部分标准类型映射成短名比如ClaimTypes.Role映射成“role”。解决最省心的方法是写入和读取都直接用ClaimTypes.*常量不自行发明字符串。如果非要自定义claim名就在JwtBearer配置里设置options.MapInboundClaims false关闭默认映射然后读写都用自己的类型字符串。这个选项很多人不知道排查的时候像黑匣子一样其实就是一个开关的事。6. 上线前验证curl实测与token续签思路6.1 用curl跑通登录和受保护接口源码包里的Swagger已经能完成大部分调试但自动化测试或写联调脚本时curl更直接。可以先用curl拿到token再带着token访问受保护接口。# 第一步调用登录接口获取token curl -X POST http://localhost:5000/api/Authentication/login \ -H Content-Type: application/json \ -d {userName:admin,password:123456} # 第二步把上一步返回的token粘贴到下面再请求受保护接口 curl -X GET http://localhost:5000/api/Authentication/userinfo \ -H Authorization: Bearer eyJhbGciOiJIUzI1NiIs...用curl做验证要做到两点第一步的响应里能拿到token字段第二步的响应码是200且body里出现用户信息。如果第二步是401直接按第5章顺序排查看是哪种坑。注意如果token返回到了JSON里直接复制会带引号导致发送失败可以用python或jq做提取。6.2 token续签思路与双token方案JWT是一次性无状态凭证一旦过期就只能重新登录。这在移动端场景里很烦人所以实际项目里普遍用双token方案access token有效期30分钟refresh token有效期7天。refresh token不走JWT签名而是存到Redis或数据库附带随机字符串请求刷新接口时用它换新的access token。[HttpPost(refresh)] [AllowAnonymous] public IActionResult Refresh([FromBody] RefreshRequest request) { // 1. 校验refresh token在Redis中存在且未过期 // 2. 存在则调用JwtTokenOperation重新生成access token // 3. 返回新token和新的refresh token实现token续签 }一个简化版的刷新接口应该是这样校验refresh token在Redis里存在且未过期然后重新调用JwtTokenOperation生成access token。要点是刷新后要让旧refresh token失效避免token被反复使用。这块如果做深还要考虑token轮换、设备下线通知、密码修改后吊销所有token但这些都要结合你自己的存储和业务设计源码包给出的是最干净的起点。每次跑完这套流程我都会把“登录-访问-刷新”三步用curl串一遍强制走完哪怕只是一个人在本地调试也如此因为token鉴权的问题往往在第二次、第三次请求时才会暴露。这也是拆这个源码包给我留下的习惯。希望帮到你。本文还有配套的精品资源点击获取

相关新闻

NDIS协议驱动开发实战:从原始帧抓包到外网接口驱动实现

NDIS协议驱动开发实战:从原始帧抓包到外网接口驱动实现

简介:面向Windows底层网络驱动开发者的资料包,聚焦NDIS(网络驱动接口标准)与协议驱动开发,适合需要理解网络栈中间层、编写或调试驱动程序的工程师,也可为研究拨号、无线或虚拟适配器等外网接口驱动的开发者…

2026/10/9 3:08:56 阅读更多 →
GitHub Trending高星与高增项目盘点:从star增速到开源选型指南

GitHub Trending高星与高增项目盘点:从star增速到开源选型指南

每天早上打开 GitHub Trending 已经成了我的固定动作,今天(1月13日)的榜单尤其有意思:一眼扫过去,既有那种稳稳涨了好几个月的高星项目,也有昨天还没影儿、今天就窜到前列的新面孔。我做开源项目日报不是第…

2026/10/9 3:07:55 阅读更多 →
手写动态线程池:不重启即可调整线程数与队列容量

手写动态线程池:不重启即可调整线程数与队列容量

你有没有过这种经历:线上服务正在扛一波流量洪峰,线程池被打满,任务队列越排越长,接口RT直线飙升。你瞄了一眼监控,心里很清楚——只要把最大线程数从 10 调到 50,或者把队列容量放大一点,就能扛…

2026/10/9 3:07:55 阅读更多 →

最新新闻

叉车装上“智慧之眼”:RFID天线如何让仓储搬运秒级精准识别

叉车装上“智慧之眼”:RFID天线如何让仓储搬运秒级精准识别

在电商、制造、冷链等行业高速发展的今天,仓储管理正从“人力驱动”向“数据驱动”转变。叉车作为仓储作业的核心设备,其运行效率与作业准确性直接决定了仓库的整体效能。然而,传统的叉车作业模式中,操作员需频繁停车进行人工扫码…

2026/10/9 5:10:15 阅读更多 →
基于Nexus 7000的数据中心网络建设方案:从vPC到安全域划分的落地实践

基于Nexus 7000的数据中心网络建设方案:从vPC到安全域划分的落地实践

简介:这份《数据中心建设方案》文档面向网络工程师、系统架构师及信息化项目规划人员,系统讲解数据中心从架构设计到落地实施的关键环节,帮助读者理解如何构建高可用、可扩展且绿色节能的数据中心。资源包内含1个doc文件,大小约2.…

2026/10/9 5:10:15 阅读更多 →
基于SSM框架的医院住院管理系统:设计与实现全解析

基于SSM框架的医院住院管理系统:设计与实现全解析

1. 项目整体设计与功能拆解1.1 为什么是SSM,这套组合到底香在哪SSM医院住院管理系统,光看这个名字,Spring、SpringMVC、MyBatis这三件套就已经在脑子里自动跑起来了。说实话,这几年找我帮忙看代码的师弟师妹,十个里有八…

2026/10/9 5:10:15 阅读更多 →
基于 gh CLI 的 GitHub Issue/PR 积压智能分诊:github-triage 插件深度指南

基于 gh CLI 的 GitHub Issue/PR 积压智能分诊:github-triage 插件深度指南

AI 技能AI 插件应用安全网络安全AI 评测 【免费下载链接】skills Trail of Bits Claude Code skills for security research, vulnerability detection, and audit workflows 项目地址: https://gitcode.com/gh_mirrors/skills8/skills 点击查看 免费下载 导读 本…

2026/10/9 5:10:15 阅读更多 →
山东专升本计算机500个知识点总结:从知识索引到三轮复习的高效用法

山东专升本计算机500个知识点总结:从知识索引到三轮复习的高效用法

简介:面向山东专升本计算机文化基础备考的五百个重要知识点总结,覆盖计算机发展史、冯诺依曼存储程序概念、语言处理程序三个阶段、计算机发展阶段划分、中央处理器与算术逻辑单元功能、总线组成、操作系统任务与数据库管理等核心内容,以问答…

2026/10/9 5:10:15 阅读更多 →
马尾辫模拟技术原理与工程实践

马尾辫模拟技术原理与工程实践

我无法根据当前输入生成符合要求的博文。原因如下:项目标题“ponytail”本身是一个英文普通名词,意为“马尾辫”,属于日常发型术语,但未提供任何具体项目背景、技术指向、应用场景或领域归属(如时尚造型教程、3D建模中…

2026/10/9 5:09:15 阅读更多 →

日新闻

Java时间API实战:LocalDate、Date与ZonedDateTime的转换与避坑指南

Java时间API实战:LocalDate、Date与ZonedDateTime的转换与避坑指南

Java时间API这个话题,隔三差五就会在群里被翻出来讨论一次。上周还有个同事线上处理一个订单超时问题,排查到最后发现是ZonedDateTime序列化后时区丢了,用户在下单当天晚上看到的时间整整差了8个小时。这类问题几乎每个做Java开发的人都遇到过…

2026/10/9 0:00:49 阅读更多 →
EasyTier实践:从NAT穿透到子网代理的异地组网部署与排错

EasyTier实践:从NAT穿透到子网代理的异地组网部署与排错

前几个月我手头有好几台机器需要互相访问:办公室台式机、家里 NAS、还有一台云主机。如果只是偶尔传个文件倒还好,问题是工作场景经常要在几处环境之间来回切换,每次都先登录跳板机再层层代理,实在折腾。我先后试过端口映射、自建…

2026/10/9 0:00:49 阅读更多 →
AI Agent工程实战:从七要素到七个决策点的系统设计指南

AI Agent工程实战:从七要素到七个决策点的系统设计指南

AI Agent 这个词在过去一年里被反复提及,但真正动手搭过一套能跑起来的 Agent 系统的人都知道,从"知道它是什么"到"让它稳定干活"之间隔着一整套工程决策。我前后参与过几个 Agent 项目的落地,从最初用现成框架拼装&…

2026/10/9 0:01:50 阅读更多 →

周新闻

KT148A语音芯片外挂8002D功放的工程实践指南

KT148A语音芯片外挂8002D功放的工程实践指南

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

2026/10/8 15:26:32 阅读更多 →
LLC谐振变换器增益公式推导:从FHA等效到完整归一化表达式

LLC谐振变换器增益公式推导:从FHA等效到完整归一化表达式

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

2026/10/8 15:26:40 阅读更多 →
ARM架构深度解析:从RISC设计理念到交叉编译实战

ARM架构深度解析:从RISC设计理念到交叉编译实战

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

2026/10/8 10:10:36 阅读更多 →

月新闻

我发现了一个新思路:用 Remotion + Claude Code 像写代码一样自动化生成短视频

我发现了一个新思路:用 Remotion + Claude Code 像写代码一样自动化生成短视频

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

2026/10/8 21:13:17 阅读更多 →
Windows下 Codex 中 Chrome 和 Computer Use 插件不可用问题排查及解决参考方式:TaoToken 统一 Key 配置与验证

Windows下 Codex 中 Chrome 和 Computer Use 插件不可用问题排查及解决参考方式:TaoToken 统一 Key 配置与验证

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

2026/10/8 15:26:17 阅读更多 →
黑夜航拍船只数据集训练YOLOV5模型全流程解析

黑夜航拍船只数据集训练YOLOV5模型全流程解析

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

2026/10/7 13:34:55 阅读更多 →