ARTICLE DETAIL

建站实战干货

来自一线的建站与推广经验沉淀,每一条都经过真实交付验证。

Token过期判断与自动更新:JWT、Refresh Token 面试实战全解析

2026/8/30 2:14:38 拓冰建站 浏览量
Token过期判断与自动更新:JWT、Refresh Token 面试实战全解析 软件测试面试中Token 相关题目出现频率很高。面试官问“如何判断 Token 是否过期自动更新 Token 要如何实现”表面在考 API 鉴权实际在考你有没有真正处理过登录态的生命周期。因为 Token 不是神秘字符串它是一段带有签名和时效的凭证登录也不是“登录成功就完事”而是过期时间、续期方式、并发刷新、服务端失效策略这一整套链路要能跑通。这篇文章从 Cookie、Session、Token 的区别开始讲到 JWT 的过期校验和 Refresh Token 自动续期最后给出可运行的代码、验证方法和面试答题思路。1. 面试官问 Token 过期到底在问什么1.1 Token 不是登录状态本身而是登录凭证的表示Token 是客户端访问受保护资源时携带的凭证。服务端登录成功后签发 Token客户端在后续请求中通过请求头、请求参数或 Cookie 携带它。服务端收到请求后需要验证 Token 是否有效、是否被篡改、是否已经过期然后才决定是否放行。这里要区分两个概念登录状态服务端认为某个用户已经完成身份认证并且这个状态仍然有效。Token用户持有的凭证服务端通过验证它可以判断“当前请求是谁发起的、凭证是否还在有效期内”。在传统 Cookie Session 方案里登录状态记录在服务端内存或 Redis 中客户端只保存一个 sessionId。Token 方案则把用户信息和过期时间编码进 Token 本身服务端签名验证通过后即可信任。两者不是互斥的很多系统会同时使用 Cookie、Session、Token面试中经常要求你讲清楚它们的区别。1.2 判断 Token 是否过期的三类方法面试官问“如何判断 Token 是否过期”通常期待的不是某一个答案而是你能否按“客户端、服务端、存储层”三个位置分别说明。判断位置实现方式可靠性适用场景客户端本地解析 Token 中的过期时间与本地当前时间比较依赖客户端时钟可被修改仅作为体验优化提前弹出“登录即将过期”提示服务端解析验证签名后读取过期时间字段并比较当前时间不依赖客户端但 Token 在有效期内无法立刻失效常规 API 鉴权服务端状态查询查询 Redis 或数据库中的 Token 状态、黑名单、版本号可主动踢人、立即失效、支持单点登录控制高安全要求的后台系统实际项目里这三类方法往往是组合使用的。前端在本地提前判断是为了减少无效请求服务端解析判断是为了保证接口安全状态查询是为了解决“修改密码后旧 Token 仍然有效”这类问题。1.3 Token 生命周期中的关键时间点很多开发者在代码里只关注一个过期时间导致面试时讲不清楚。完整的 Token 生命周期至少包括以下时间点iat签发时间表示 Token 从什么时候开始生效。exp过期时间表示 Token 在什么时候之后不再有效。nbf生效时间表示 Token 在什么时候之前不应被接受。Refresh Token 的过期时间用于换新 Access Token 的凭证通常比 Access Token 长。服务端允许的时钟偏差分布式环境下签发服务和校验服务的时间可能不完全同步需要使用 leeway 容忍少量偏差。判断 Token 是否过期核心是检查exp字段而不是检查 Token 是否存在于某个集合中。这也是 JWTJSON Web Token与普通随机 Token 的显著差异JWT 自包含过期信息服务端校验签名后就能得到过期时间。2. 从 Cookie、Session 到 Token先理清三种会话机制2.1 Cookie Session 的过期判断在传统 Web 应用中用户登录成功后服务端会创建一个 Session 对象并生成一个 sessionId把 sessionId 写入 Cookie 返回给浏览器。浏览器后续请求自动携带这个 Cookie服务端根据 sessionId 找到对应的 Session 数据。Session 的过期判断由服务端控制常见方式有两种设置 Session 超时时间例如 30 分钟无访问则失效。设置 Cookie 的过期时间例如浏览器关闭后失效。这种方式的问题在于Session 数据保存在服务端多节点部署时必须引入共享存储否则用户请求到另一台机器就找不到 Session。此外前后端分离项目中App 和第三方客户端不一定能方便地管理 Cookie因此 Token 方案逐渐流行。2.2 Token 与 JWT 的出现动机Token 方案的核心思路是服务端不再保存一份完整的会话数据而是把用户标识、权限、过期时间等信息打包进 Token用签名保证它不被篡改。服务端只需要验证签名和过期时间就能确认请求身份。JWT 是 Token 的一种标准格式由三部分组成Header声明签名算法和 Token 类型。Payload存放用户信息、过期时间等声明。Signature对 Header 和 Payload 的签名结果。JWT 的 Payload 是 Base64Url 编码的不是加密的因此不能把密码等敏感信息放进去。面试中如果被问“JWT 安全吗”可以回答它只能保证内容不被篡改不能保证内容不可见敏感信息应该放在服务端而不是 Token 里。2.3 Access Token 与 Refresh Token 的职责拆分为什么不能只发一个长时间有效的 Token如果 Access Token 设置 7 天有效客户端泄露后攻击者 7 天内都能使用。如果设置 15 分钟有效用户每 15 分钟就要重新登录一次体验很差。解决办法是引入双 Token 机制Access Token有效期短例如 30 分钟用来访问业务接口。Refresh Token有效期长例如 7 天只用来换取新的 Access Token。Refresh Token 相当于长期登录凭证。Access Token 过期后客户端用 Refresh Token 请求刷新接口服务端验证 Refresh Token 有效后签发新的 Access Token 和新的 Refresh Token。这样既缩短了泄露风险暴露时间又不需要用户频繁登录。需要注意的是Refresh Token 的权限范围应该尽量窄通常只允许调用刷新接口不允许直接访问业务接口。同时Refresh Token 也需要服务端保存状态否则你无法在用户修改密码、退出登录后让它立即失效。3. 如何判断 Token 是否过期代码层面实现3.1 准备一个最小后端工程下面示例使用 Spring Boot jjwt 实现 JWT 的签发和校验。实际项目可以用 java-jwt、auth0、Nimbus JOSE JWT 等库思路一致。先准备一个 JwtTokenUtil负责生成 Token、解析 Token、判断过期。代码中的密钥只是演示用生产环境必须从配置中心或环境变量读取。import io.jsonwebtoken.Claims; import io.jsonwebtoken.Jwts; import io.jsonwebtoken.SignatureAlgorithm; import io.jsonwebtoken.security.Keys; import javax.crypto.SecretKey; import java.util.Date; public class JwtTokenUtil { private static final SecretKey KEY Keys.hmacShaKeyFor( please-change-this-secret-key-please-change-this-secret.getBytes()); private static final long ACCESS_TOKEN_EXPIRE_MS 30 * 60 * 1000L; public static String generateToken(String userId, String username) { Date now new Date(); Date expire new Date(now.getTime() ACCESS_TOKEN_EXPIRE_MS); return Jwts.builder() .setSubject(userId) .claim(username, username) .setIssuedAt(now) .setExpiration(expire) .signWith(KEY, SignatureAlgorithm.HS256) .compact(); } public static Claims parseToken(String token) { return Jwts.parserBuilder() .setSigningKey(KEY) .build() .parseClaimsJws(token) .getBody(); } public static boolean isTokenExpired(String token) { try { Claims claims parseToken(token); return claims.getExpiration().before(new Date()); } catch (Exception e) { return true; } } }这里有几个关键点setExpiration会把过期时间写入 JWT 的exp字段。parseClaimsJws在 Token 过期时会抛出ExpiredJwtException在签名不正确时会抛出SignatureException。不要在isTokenExpired里把所有异常都吞掉而不做区分否则你无法判断是过期还是伪造。3.2 在服务端过滤器中统一校验 Token判断过期不能只靠业务代码里手动调用最好放在过滤器或拦截器里统一处理。下面使用 Spring Boot 的OncePerRequestFilter演示。import io.jsonwebtoken.Claims; import io.jsonwebtoken.ExpiredJwtException; import io.jsonwebtoken.JwtException; import javax.servlet.FilterChain; import javax.servlet.ServletException; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; import org.springframework.stereotype.Component; import org.springframework.web.filter.OncePerRequestFilter; import java.io.IOException; Component public class JwtAuthenticationFilter extends OncePerRequestFilter { Override protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain chain) throws ServletException, IOException { String header request.getHeader(Authorization); if (header ! null header.startsWith(Bearer )) { String token header.substring(7); try { Claims claims JwtTokenUtil.parseToken(token); request.setAttribute(userId, claims.getSubject()); request.setAttribute(username, claims.get(username)); } catch (ExpiredJwtException e) { response.setStatus(401); response.getWriter().write({\code\:401,\message\:\token expired\}); return; } catch (JwtException | IllegalArgumentException e) { response.setStatus(401); response.getWriter().write({\code\:401,\message\:\invalid token\}); return; } } chain.doFilter(request, response); } }过滤器的作用是把“Token 是否有效、是否过期”的校验从业务代码中抽离出来。业务接口只需要从请求上下文里取用户信息不需要每个接口都写一遍解析逻辑。3.3 前端提前判断为了体验不是为了安全前端也可以提前判断 Token 是否快要过期。做法是解析 JWT 的 Payload读取exp字段然后与本地时间比较。function getTokenExpireTime(token) { const payload token.split(.)[1]; const base64 payload.replace(/-/g, ).replace(/_/g, /); const decoded JSON.parse(decodeURIComponent(escape(window.atob(base64)))); return decoded.exp * 1000; } function isTokenWillExpire(token, leadTimeMs 5 * 60 * 1000) { const exp getTokenExpireTime(token); return exp - Date.now() leadTimeMs; }前端提前判断的作用是减少无效请求例如在 Access Token 还有 5 分钟过期时提前调用刷新接口。但严格来说前端判断不能作为安全依据因为用户修改本地系统时间会直接影响Date.now()的结果。客户端代码可以被篡改前端判断结果不可信。多个服务端节点之间可能存在时间偏差。真正的过期判断必须发生在服务端。3.4 过期判断的常见误区这里汇总几个常见错误面试时说出来会显得你有实战经验。错误写法错误原因推荐做法只验证签名不检查exp过期 Token 仍然能通过校验解析后必须比较过期时间捕获所有异常后统一返回“token 无效”无法区分过期、伪造、格式错误优先捕获过期异常给出独立错误码使用客户端当前时间判断服务端 Token 是否过期依赖用户时钟不安全服务端统一使用服务器时间判断把 Token 有效期内无法失效当成正常现象用户修改密码后旧 Token 仍可用引入 Token 版本号或 Redis 黑名单4. 自动更新 TokenRefresh Token 续期机制完整实现4.1 场景设定和设计思路假设一个前后端分离项目使用 Spring Boot 提供 API前端使用 Axios 发送请求。登录成功后后端返回两个值accessToken30 分钟有效用于调用业务接口。refreshToken7 天有效用于换取新的 Access Token。自动更新流程如下用户登录获得 Access Token 和 Refresh Token。前端携带 Access Token 调用业务接口。服务端发现 Access Token 过期返回 401。前端拦截 401携带 Refresh Token 请求刷新接口。服务端验证 Refresh Token生成新的 Access Token 和新的 Refresh Token。前端用新 Token 重发原来的业务请求。这样用户无感知地完成续期不需要重新输入密码。4.2 后端签发 Access Token 与 Refresh Token在 JwtTokenUtil 中增加 Refresh Token 的生成方式。Refresh Token 可以也使用 JWT但必须有独立的过期时间。public static String generateAccessToken(String userId, String username) { Date now new Date(); Date expire new Date(now.getTime() 30 * 60 * 1000L); return Jwts.builder() .setSubject(userId) .claim(username, username) .setIssuedAt(now) .setExpiration(expire) .signWith(KEY, SignatureAlgorithm.HS256) .compact(); } public static String generateRefreshToken(String userId) { Date now new Date(); Date expire new Date(now.getTime() 7 * 24 * 60 * 60 * 1000L); return Jwts.builder() .setSubject(userId) .setIssuedAt(now) .setExpiration(expire) .signWith(KEY, SignatureAlgorithm.HS256) .compact(); }登录接口成功之后同时返回两个 Token。真实项目中密码校验应该使用 BCrypt 等哈希算法不要明文存储。public LoginResponse login(String username, String password) { // 校验用户密码这里省略数据库查询和密码比对 String userId 1001; String accessToken JwtTokenUtil.generateAccessToken(userId, username); String refreshToken JwtTokenUtil.generateRefreshToken(userId); // 生产环境建议把 refreshToken 哈希后保存到 Redis并绑定用户、设备、会话 id refreshTokenService.save(userId, refreshToken); return new LoginResponse(accessToken, refreshToken, 30 * 60); }为什么 Refresh Token 要保存到服务端因为 JWT 本身无法主动失效。用户点了退出登录如果服务端不记录 Refresh Token 状态这个 Refresh Token 在 7 天内仍然可以换取新 Token。只靠 JWT 的过期时间无法实现“立即退出”和“修改密码后旧会话失效”。4.3 刷新接口与 Refresh Token 轮换刷新接口的核心逻辑是接收 Refresh Token校验有效性和是否存在然后生成新的 Access Token 和新的 Refresh Token同时把旧 Refresh Token 标记为已使用或已失效。PostMapping(/refresh) public ResponseEntity? refresh(RequestBody RefreshRequest request) { String oldRefreshToken request.getRefreshToken(); RefreshTokenInfo info refreshTokenService.validateAndConsume(oldRefreshToken); if (info null) { return ResponseEntity.status(401).body(refresh token expired or used); } String userId info.getUserId(); String username info.getUsername(); String newAccessToken JwtTokenUtil.generateAccessToken(userId, username); String newRefreshToken JwtTokenUtil.generateRefreshToken(userId); refreshTokenService.revoke(oldRefreshToken); refreshTokenService.save(userId, newRefreshToken); return ResponseEntity.ok(new LoginResponse(newAccessToken, newRefreshToken, 30 * 60)); }这里有两个容易忽略的设计点每次刷新都生成新的 Refresh Token把旧 Token 失效称为 Refresh Token 轮换。如果客户端把同一个 Refresh Token 并发发送多次第一个请求成功后后面的请求会因为旧 Token 已失效而返回 401。这既是一种安全设计也要求前端做好并发刷新合并。4.4 前端 Axios 拦截器自动刷新前端在使用 Axios 时通过响应拦截器处理 401。核心代码逻辑如下。axios.interceptors.response.use( (response) response, async (error) { const { response, config } error; if (response response.status 401 !config._retry config.url ! /api/auth/refresh) { config._retry true; const refreshToken localStorage.getItem(refreshToken); if (!refreshToken) { redirectToLogin(); return Promise.reject(error); } try { const { data } await axios.post(/api/auth/refresh, { refreshToken }); localStorage.setItem(accessToken, data.accessToken); localStorage.setItem(refreshToken, data.refreshToken); config.headers[Authorization] Bearer data.accessToken; return axios(config); } catch (refreshError) { redirectToLogin(); return Promise.reject(refreshError); } } return Promise.reject(error); } );这段代码的关键点config._retry用于标记请求已经重试过防止刷新接口再次失败后无限循环。刷新接口本身不应该进入这个重试逻辑否则会递归调用。刷新成功后要更新本地保存的 Access Token并重新设置原请求的 Authorization 请求头。刷新失败后说明 Refresh Token 已失效应该跳转到登录页。注意localStorage 存储 Token 会面临 XSS 风险。如果页面被注入恶意脚本攻击者可以直接读取 Token。生产环境可以改用 httpOnly Cookie 存储 Refresh Token或者使用更严格的内存存储方案但内存存储会丢失刷新能力需要结合具体场景权衡。4.5 并发请求 401 时的刷新合并一个页面上可能同时发起多个请求如果 Access Token 恰好过期这些请求会同时收到 401然后各自触发一次刷新。如果后端 Refresh Token 轮换多个并发刷新请求使用同一个旧 Refresh Token会导致后面的刷新请求全部失败。解决办法是在前端做“单飞”刷新多个请求共享同一个刷新 Promise只发一次刷新请求。let refreshPromise null; async function refreshTokenOnce() { if (!refreshPromise) { refreshPromise axios.post(/api/auth/refresh, { refreshToken: localStorage.getItem(refreshToken) }).then(({ data }) { localStorage.setItem(accessToken, data.accessToken); localStorage.setItem(refreshToken, data.refreshToken); return data; }).finally(() { refreshPromise null; }); } return refreshPromise; }在响应拦截器中调用refreshTokenOnce()而不是每次都直接axios.post。这样多个 401 请求会排队等待同一个刷新请求刷新完成后分别用新 Token 重试各自的原始请求。后端也可以做缓冲在很短的时间窗口内如果同一个 Refresh Token 被重复使用只要它已经成功换取过一次新 Token就允许短时间内容错返回同一个新 Token而不是直接拒绝。但这个方案会增加状态管理复杂度建议先从前端并发合并做起。5. 运行验证与日志检查5.1 测试用例设计如果你负责测试这套登录体系以下用例是必须覆盖的。用例名称前置条件操作步骤预期结果关注点正常登录用户已注册调用登录接口返回 Access Token 和 Refresh TokenToken 是否包含预期用户信息未过期 Token 访问接口登录成功携带 Access Token 调用业务接口返回 200过滤器正常放行Access Token 过期Token 已过期携带过期 Token 调用接口返回 401 且提示 token expired错误码是否明确Refresh Token 刷新成功Refresh Token 有效调用刷新接口返回新 Token旧 Refresh Token 是否失效Refresh Token 重复使用已成功刷新一次再次使用旧 Refresh Token 刷新返回 401是否执行轮换Refresh Token 过期等待超过有效期调用刷新接口返回 401过期判断是否生效修改密码后刷新用户修改密码使用修改前 Token 刷新返回 401服务端是否绑定版本号测试时不要只看 HTTP 状态码还要检查响应体里的错误信息、新 Token 的过期时间是否符合预期、旧 Token 是否还能使用。5.2 使用 curl 验证核心链路先登录拿到 Token。curl -X POST http://localhost:8080/api/auth/login \ -H Content-Type: application/json \ -d {username:admin,password:123456}登录成功后会返回类似下面的 JSON。{ accessToken: eyJhbGciOiJIUzI1NiJ9..., refreshToken: eyJhbGciOiJIUzI1NiJ9..., expiresIn: 1800 }然后访问业务接口。curl http://localhost:8080/api/user/info \ -H Authorization: Bearer accessToken如果 Access Token 过期会看到 401。此时使用 Refresh Token 调用刷新接口。curl -X POST http://localhost:8080/api/auth/refresh \ -H Content-Type: application/json \ -d {refreshToken:refreshToken}验证刷新成功之后再次使用旧 Refresh Token 重复调用刷新接口应该收到 401。这一步是为了确认轮换逻辑生效。5.3 常见错误现象与排查链路实际接入第三方登录、OAuth、企业微信登录时还可能出现 Token 交换阶段的错误。这里整理一份常见的排查表。错误现象常见原因检查方式接口返回 401提示 token expiredAccess Token 过期查看 Token 的exp时间确认是否过期然后走刷新流程接口返回 401提示 invalid token签名错误、Token 被篡改、密钥不匹配检查签发和校验是否使用同一密钥检查 Token 内容刷新接口返回 401Refresh Token 过期、已被使用、服务端不存在查询 Redis 或数据库中的 Refresh Token 记录前端出现无限重试拦截器没有排除刷新接口在拦截器中排除/api/auth/refresh多个并发请求导致刷新失败多个 401 同时发起刷新旧 Refresh Token 已失效前端做刷新单飞后端考虑短时间容错第三方登录时提示 sign-in could not be completed token exchange failed身份提供方IdP的授权配置、回调地址、可用区域策略异常检查第三方应用的注册信息、回调地址、IdP 是否允许当前网络出口区域访问并查看 IdP 返回的具体状态码其中sign-in could not be completed token exchange failed: token endpoint returned status 403 forbidden: country, region, or territory not supported这类错误通常不是你的代码逻辑问题而是身份提供方在 Token 交换阶段做区域策略拦截。排查时先到 IdP 控制台确认应用是否启用了正确的登录和令牌权限。回调地址是否与代码里配置的一致。当前网络出口所在区域是否被 IdP 允许访问。不要尝试绕过区域策略应该由业务方根据合规要求调整部署区域或联系 IdP 支持。5.4 测试时如何观察刷新是否真的发生很多测试只关注“最终请求成功”但刷新是否真的发生、刷新了多少次、旧 Token 是否失效往往被忽略。测试时可以在前端 Network 面板里观察请求顺序请求/api/user/info返回 401。自动请求/api/auth/refresh返回 200。重新请求/api/user/info返回 200。在后端日志中打印关键节点收到登录请求。签发 Access Token 和 Refresh Token。收到刷新请求校验 Refresh Token。旧 Refresh Token 已失效。生成新 Token。日志中不要打印完整 Token 内容Token 属于敏感信息只打印用户 ID、Token 前缀或最后几位即可。6. 从面试到落地最佳实践与扩展方向6.1 生产环境不是写完刷新就结束了自动更新 Token 能跑通只代表最小闭环完成。生产环境还需要补充以下能力密钥外置化JWT 签名密钥从配置中心读取不能硬编码到代码仓库。Redis 保存 Refresh Token保存 Token 哈希、用户 ID、设备信息、过期时间。设备管理和踢人下线记录会话 ID支持管理员强制下线。统一的注销接口同时注销 Access Token、Refresh Token 和会话状态。登录日志与审计记录登录时间、IP、设备、刷新行为便于安全分析。接口权限校验Access Token 通过后还要根据用户角色和权限判断是否允许访问资源。区分学习环境和生产环境是一个加分回答。学习环境中可以用一个 Map 保存 Refresh Token但生产环境必须使用带过期策略的 Redis并且要处理并发、数据一致性、日志链路。6.2 Token 黑名单、并发刷新与时钟偏差面向面试的进阶问题你应该主动展开以下三点。第一Access Token 已经过期但 Redis 中仍存在怎么办如果系统需要在 Access Token 有效期内踢人可以引入黑名单用户退出或修改密码时把当前 Access Token 的jti或 Token 哈希写入 Redis 黑名单并设置过期时间等于 Token 剩余有效期。第二Refresh Token 轮换后的并发问题。前端和后端都需要处理前端用共享 Promise 合并刷新请求。后端使用 Lua 脚本或数据库事务保证 Refresh Token 的“读取、校验、失效、替换”具备原子性。Redis 中可以使用字符串记录 Refresh Token 哈希使用GETDEL或 Lua 脚本实现一次性消费。第三时钟偏差。跨服务器部署时签发服务器和校验服务器的系统时间可能相差几秒。如果 A 服务器签发了一个过期时间精确到秒的 TokenB 服务器可能提前判定它过期。使用 jjwt 时可以设置允许的时钟偏差。Jwts.parserBuilder() .setSigningKey(KEY) .setAllowedClockSkewSeconds(30) .build() .parseClaimsJws(token);setAllowedClockSkewSeconds允许在过期时间之后 30 秒内仍然认为 Token 有效避免因服务器时钟抖动导致误判。6.3 给测试工程师的面试回答建议面试时不要直接背“过期就 401然后刷新”。推荐按下面这个结构回答先给结论判断 Token 是否过期必须由服务端校验签名和exp字段前端判断只能作为体验优化。再给方案登录时返回 Access Token 和 Refresh TokenAccess Token 短时效Refresh Token 长时效。说实现链路前端拦截 401调用刷新接口后端校验 Refresh Token 后轮换并返回新 Token前端重发原请求。说出关键坑并发刷新会共享同一个 Refresh Token前端要做单飞Refresh Token 不轮换就无法感知重放。最后说怎么测试覆盖登录、过期访问、刷新成功、刷新失败、重复使用、并发刷新、修改密码后失效。这个结构能让面试官看到你不只写过一个 “if 过期 return false”而是考虑到了完整状态流转。6.4 可复用的 Token 测试清单无论你是开发还是测试动手验证前都可以先按下面清单检查环境[ ] 后端密钥已配置没有使用硬编码默认值。[ ] Access Token 过期时间已配置例如 30 分钟。[ ] Refresh Token 过期时间长于 Access Token例如 7 天。[ ] 刷新接口不要求携带 Access Token。[ ] 刷新接口会返回新的 Access Token 和 Refresh Token。[ ] 旧 Refresh Token 在刷新后已失效。[ ] 前端拦截器只对业务接口触发刷新不会递归刷新刷新接口。[ ] 多个 401 请求并发时只发起一次刷新请求。[ ] 修改密码或退出登录后旧 Refresh Token 无法继续刷新。[ ] 日志中不打印完整 Token 内容。6.5 学习路径清单如果这套机制还不熟悉可以按下面的顺序练习理解 Cookie、Session、Token、JWT 的关系和区别。手写一个 JWT 签发与校验工具熟悉exp、iat、nbf。在 Spring Boot 过滤器中接入 Token 校验。加入 Refresh Token 刷新接口测试轮换逻辑。使用 Redis 保存 Refresh Token支持主动失效。在 Axios 中实现响应拦截器自动刷新。模拟并发请求验证刷新单飞是否生效。编写异常场景测试Token 过期、伪造签名、重复刷新、时钟偏差。技术面试的答案并不是背诵八股。能说清楚为什么 Access Token 不能太长、Refresh Token 为什么要轮换、并发刷新会出现什么问题能现场画出请求链路已经超过大部分候选者。下一步建议自己动手搭一个 Spring Boot Redis Axios 的最小登录项目把 401 刷新流程完整跑一遍。跑通之后再故意写错密钥、改短过期时间、并发发起多个请求你会真正记住 Token 失效和自动续期背后的设计逻辑。