.NET Core Web API中JWT闲置快速过期问题求助
嘿,这个问题我之前也踩过坑!核心是你搞混了JWT本身的过期机制和Cookie/Session的过期机制,这俩完全是两回事儿,咱们一步步理清楚:
问题根源拆解
首先,JWT是无状态令牌,它的过期时间是在生成时就写死在token的exp字段里的——你生成代码里已经设了expires: DateTime.UtcNow.AddYears(1),那这个token本身确实是1年后才会过期。那为啥闲置几分钟就失效?
看你的启动代码,你同时配置了JWT Bearer认证、Cookie认证和Session,问题就出在这儿:你大概率是把JWT存在了Cookie或者Session里,而Cookie/Session的过期时间设的是60分钟,Cookie过期了,你拿不到token了,不是JWT本身过期。
另外,SlidingExpiration是Cookie认证的特性,和JWT半毛钱关系没有——它只是帮你延长Cookie的有效期,不是更新JWT的exp字段。你发虚拟请求能维持有效性,其实是在刷新Cookie的过期时间,不是JWT的。
解决方案分两种场景
根据你的需求(关闭网页后token仍有效),分两种情况处理:
场景1:纯Web API(前端是SPA/小程序等)
纯API场景根本不需要Cookie和Session,直接让前端把JWT存在localStorage里,每次请求放在Authorization头里(格式是Bearer <token>)。这样即使关闭网页,localStorage里的token还在,只要没到exp时间就能继续用。
修改你的启动代码,去掉所有Cookie/Session相关配置,只保留JWT Bearer:
services.AddAuthentication(JwtBearerDefaults.AuthenticationScheme) .AddJwtBearer(x => { string secretKeyStr = Configuration.GetValue<string>("SecretKey"); byte[] secretKey = Encoding.ASCII.GetBytes(secretKeyStr); x.RequireHttpsMetadata = false; x.SaveToken = true; x.TokenValidationParameters = new TokenValidationParameters { ValidateIssuerSigningKey = true, IssuerSigningKey = new SymmetricSecurityKey(secretKey), ValidateIssuer = false, ValidateAudience = false, RequireExpirationTime = true, ValidateLifetime = true, ClockSkew = TimeSpan.FromDays(1), }; }); // 删掉这些不需要的配置: // services.AddCookie(...); // services.Configure<CookiePolicyOptions>(...); // services.AddDistributedMemoryCache(); // services.AddSession(...); // services.ConfigureApplicationCookie(...);
场景2:必须用Cookie存储JWT(比如传统MVC/混合场景)
如果一定要用Cookie存JWT,那得调整Cookie配置,让它成为持久Cookie(默认Cookie是会话级的,关闭浏览器就失效),并且Cookie的过期时间要和JWT的exp字段匹配:
services.AddAuthentication(CookieAuthenticationDefaults.AuthenticationScheme) .AddJwtBearer(x => { string secretKeyStr = Configuration.GetValue<string>("SecretKey"); byte[] secretKey = Encoding.ASCII.GetBytes(secretKeyStr); x.RequireHttpsMetadata = false; x.SaveToken = true; x.TokenValidationParameters = new TokenValidationParameters { ValidateIssuerSigningKey = true, IssuerSigningKey = new SymmetricSecurityKey(secretKey), ValidateIssuer = false, ValidateAudience = false, RequireExpirationTime = true, ValidateLifetime = true, ClockSkew = TimeSpan.FromDays(1), }; }) .AddCookie(CookieAuthenticationDefaults.AuthenticationScheme, options => { // Cookie过期时间和JWT一致,设为1年 options.ExpireTimeSpan = TimeSpan.FromYears(1); // 开启滑动过期,每次请求自动刷新Cookie过期时间 options.SlidingExpiration = true; // 关键:设置Cookie为持久Cookie,关闭浏览器后不会立即失效 options.Cookie.Expiration = TimeSpan.FromYears(1); // 其他安全配置按需调整 options.Cookie.SameSite = SameSiteMode.None; options.Cookie.SecurePolicy = CookieSecurePolicy.Always; options.Cookie.IsEssential = true; options.Cookie.HttpOnly = true; }); // 如果用了ASP.NET Core Identity,同步调整ApplicationCookie配置 services.ConfigureApplicationCookie(options => { options.SlidingExpiration = true; options.ExpireTimeSpan = TimeSpan.FromYears(1); options.Cookie.Expiration = TimeSpan.FromYears(1); });
额外提醒:关于滑动过期的替代方案
如果你的需求是“用户活跃时自动延长token有效期”,JWT本身不支持滑动过期(因为无状态,服务器不存token状态),这时候应该实现Refresh Token机制:
- 生成JWT时同时生成一个Refresh Token(有效期更长,比如7天),存在持久Cookie里
- 当JWT快过期时,客户端用Refresh Token请求新的JWT
- 服务器验证Refresh Token有效后,返回新的JWT,这样用户不用重新登录
这个方案比用虚拟请求维持Cookie有效期更优雅,也更安全。
内容的提问来源于stack exchange,提问作者ikazrima

