ARTICLE DETAIL

资讯详情

深耕郑州网站建设与运营推广的一线实战洞察。

Token过期判断与自动刷新:双Token机制原理与实现解析

Token过期判断与自动刷新:双Token机制原理与实现解析 面试官抛出这个问题时不只是在考察你知道多少概念更是想看你有没有真正处理过接口鉴权、登录态维持、并发刷新这类线上问题。很多测试同学在做接口自动化、测试平台集成、小程序/App兼容性测试时都遇到过“Token失效”导致用例批量失败但很少去深挖背后机制。本文就把 Token 过期判断、自动更新 Token、以及高频追问的“双 Token”方案完整拆开讲既有原理也有可直接落地的代码方便你用在自己的接口测试框架或学习笔记里。说明一句本文面向软件测试工程师、测试开发、以及准备面试的初级后端同学重点是“测试视角”的完整理解所以不会只贴八股文结论而是尽量还原真实项目里的处理逻辑。1. 先从面试场景聊起这道题到底在考什么1.1 面试官为什么爱问 Token 过期面试官问“如何判断 Token 是否过期”一般不是真让你背 JWT 里的 exp 字段而是想确认三件事你是否理解 Token 的生命周期是谁定义的。你是否知道前端和后端分别从哪里感知过期。你是否考虑过 Token 过期后用户体验如何保证。例如一个 App 登录之后用户一直在浏览页面没有关闭 App第二天打开发现还在登录状态。这个“登录状态”是 Token 没变吗不一定很可能是前端在 Token 过期后自动刷新了用户无感知。如果你能把这个过程讲清楚面试官会认为你有真实项目经验。1.2 常见答案和加分答案很多人的第一反应是前端拿到 Token 后解析出过期时间和当前时间比较如果小于当前时间就认为过期。这个答案没错但太表面。加分回答会增加几层前端的判断只是“预防”真正能判定 Token 是否有效的是后端。因为 Token 有可能被服务端主动拉黑比如用户被踢下线、密码修改后所有 Token 作废此时 JWT 里的 exp 还没到但 Token 已经不可用了。所以更稳妥的做法是“本地预判断 接口请求后强制判断 自动续期兜底”。1.3 测试工程师为什么要懂这个对于做接口自动化的人来说Token 失效是脚本失败的一大来源。比如你写好了 100 条用例执行到第 47 条时登录态失效后面全部报 401。如果不懂自动刷新机制只能手动重新登录或重新抓包效率很低。所以在测试框架里实现一个“统一 Token 管理模块”往往比写一堆断言更能提升自动化稳定性。2. 判断 Token 是否过期的三种常见方式2.1 JWT Token读取 exp 过期时间JWTJSON Web Token是目前最常用的 Token 格式。它的结构是Header.Payload.Signature中间 Payload 部分可以携带很多标准字段其中exp过期时间是一个 Unix 时间戳。iat签发时间。nbf生效时间早于这个时间的 Token 不可用。示例 Payload{ sub: 10086, name: 测试账号, exp: 1717152000, iat: 1717148400 }判断过期的逻辑就是拿当前时间戳和 exp 比较long currentTime System.currentTimeMillis() / 1000; long expireTime jwt.getExpiration().getTime() / 1000; if (currentTime expireTime) { // Token 已过期 }在 Java 的 jjwt 库中可以直接用Jwts.parser()解析如果 Token 过期会抛出ExpiredJwtException异常。2.2 Redis 存储 Token 过期时间不是所有系统都用 JWT有些老项目用的是 Opaque Token也就是一串随机字符串本身不携带用户信息和过期时间。这种 Token 通常存在服务端的 Redis 中结构可能是key: login:token:{tokenValue} value: { userId, loginTime, expireTime }这样判断过期就变成了查 Redis如果 key 不存在说明 Token 不存在或已过期。如果 key 存在再比对expireTime与当前时间。这种方式的好处是服务端可以主动下线用户删掉 Redis 里的 key 即可。缺点是多一次 Redis 查询接口性能会比 JWT 本地解析稍差。2.3 后端接口实时校验还有一种“最笨但最准确”的方式就是不发有效 Token只把用户 ID 或 Token 标识传给后端后端根据用户信息动态判断。例如用户被封禁、被删除、密码被修改这些情况下 JWT 本身还是“合法的”但业务上已经不允许继续访问。所以真实项目往往用“组合校验”// 伪代码先解析 JWT再查用户状态 JwtClaims claims jwtParser.parse(token); if (claims.isExpired()) { throw new TokenExpiredException(); } User user userService.getById(claims.getUserId()); if (user null || user.getStatus() ! 1) { throw new UserDisabledException(); }这种方案在权限要求高的系统里非常常见。为了直观对比三种方式总结如下判断方式判断时机优点缺点适用场景JWT 解析 exp前端本地 / 后端解析无状态、速度快无法处理被服务端提前拉黑的情况普通 Web 应用、移动端 APIRedis 查询每次请求后端查询可主动失效、可控性强多一次网络开销需要管理会话、踢人下线的系统后端实时校验每次请求后端组合校验最准确、可结合用户状态性能开销较大权限要求高、账号状态经常变化3. 自动更新 Token从“被动过期”到“无感刷新”3.1 为什么不能每次都重新登录如果 Token 过期就要求用户重新输入用户名密码体验太差。尤其移动端产品不可能让用户每隔一小时就登录一次。所以常见的做法是引入“刷新 Token”机制Access Token访问令牌有效期短比如 30 分钟用于调用业务接口。Refresh Token刷新令牌有效期长比如 7 天或 30 天专门用来换取新的 Access Token。当 Access Token 过期时前端不跳登录页而是用 Refresh Token 去调用刷新接口拿到新的 Access Token 后继续执行原请求。3.2 双 Token 机制的工作流程一个完整的双 Token 交互流程如下1. 用户登录成功 后端返回 accessToken 和 refreshToken 2. 前端调用业务接口 请求头携带 Authorization: Bearer accessToken 3. 后端校验 accessToken 如果有效正常返回数据 4. 如果 accessToken 过期 后端返回 401 或者特定错误码 5. 前端收到 401 调用 /auth/refresh 接口携带 refreshToken 6. 后端验证 refreshToken 如果有效颁发新的 accessToken并可选刷新 refreshToken 7. 前端用新 accessToken 重放刚才失败的请求这个过程对用户完全透明用户不会感受到“登录过期”。3.3 Access Token 有效期设置建议有效期不是越长越好。测试同学在做安全测试时经常会遇到这类问题Token 有效期过长泄露后危害大有效期过短用户体验差。常见设置场景Access TokenRefresh Token普通 Web 后台30 分钟7 天移动端 App2 小时30 天第三方开放平台1 小时90 天注意这些是常见经验值具体要看业务安全需求。4. 手把手实现一个自动续期 Token 模块下面用 Java Spring Boot 为例演示一个简化版的双 Token 实现。测试人员即使不写 Java 代码也可以通过这个例子理解流程方便后续阅读开发代码、设计测试用例。4.1 项目结构先看一下项目结构src/main/java/com/example/tokenrefresh/ ├── TokenRefreshApplication.java ├── controller/ │ └── AuthController.java ├── entity/ │ └── User.java ├── interceptor/ │ └── AuthInterceptor.java ├── service/ │ └── TokenService.java └── util/ └── JwtUtil.java4.2 添加依赖在pom.xml中添加 JWT 相关依赖dependency groupIdio.jsonwebtoken/groupId artifactIdjjwt-api/artifactId version0.11.5/version /dependency dependency groupIdio.jsonwebtoken/groupId artifactIdjjwt-impl/artifactId version0.11.5/version scoperuntime/scope /dependency dependency groupIdio.jsonwebtoken/groupId artifactIdjjwt-jackson/artifactId version0.11.5/version scoperuntime/scope /dependency4.3 编写 JWT 工具类这个工具类负责生成 Token、解析 Token、判断是否过期。package com.example.tokenrefresh.util; import io.jsonwebtoken.Claims; import io.jsonwebtoken.ExpiredJwtException; import io.jsonwebtoken.Jwts; import io.jsonwebtoken.SignatureAlgorithm; import io.jsonwebtoken.security.Keys; import java.nio.charset.StandardCharsets; import java.security.Key; import java.util.Date; import java.util.HashMap; import java.util.Map; public class JwtUtil { // 生产环境不要写死在代码里建议放到配置中心或环境变量 private static final String SECRET your-256-bit-secret-key-please-change-in-production; private static final long ACCESS_TOKEN_EXPIRE 30 * 60 * 1000L; // 30分钟 private static final long REFRESH_TOKEN_EXPIRE 7 * 24 * 60 * 60 * 1000L; // 7天 private static Key getKey() { return Keys.hmacShaKeyFor(SECRET.getBytes(StandardCharsets.UTF_8)); } // 生成 accessToken public static String generateAccessToken(Long userId, String username) { MapString, Object claims new HashMap(); claims.put(userId, userId); claims.put(username, username); claims.put(tokenType, access); return Jwts.builder() .setClaims(claims) .setSubject(String.valueOf(userId)) .setIssuedAt(new Date()) .setExpiration(new Date(System.currentTimeMillis() ACCESS_TOKEN_EXPIRE)) .signWith(getKey(), SignatureAlgorithm.HS256) .compact(); } // 生成 refreshToken public static String generateRefreshToken(Long userId, String username) { MapString, Object claims new HashMap(); claims.put(userId, userId); claims.put(username, username); claims.put(tokenType, refresh); return Jwts.builder() .setClaims(claims) .setSubject(String.valueOf(userId)) .setIssuedAt(new Date()) .setExpiration(new Date(System.currentTimeMillis() REFRESH_TOKEN_EXPIRE)) .signWith(getKey(), SignatureAlgorithm.HS256) .compact(); } // 解析 Token如果过期会抛出异常 public static Claims parseToken(String token) { return Jwts.parserBuilder() .setSigningKey(getKey()) .build() .parseClaimsJws(token) .getBody(); } // 检查是否过期内部已经通过 exp 判断 public static boolean isExpired(String token) { try { parseToken(token); return false; } catch (ExpiredJwtException e) { return true; } catch (Exception e) { return true; } } // 判断 Token 类型是否为 accessToken public static boolean isAccessToken(String token) { try { Claims claims parseToken(token); return access.equals(claims.get(tokenType)); } catch (Exception e) { return false; } } // 判断 Token 类型是否为 refreshToken public static boolean isRefreshToken(String token) { try { Claims claims parseToken(token); return refresh.equals(claims.get(tokenType)); } catch (Exception e) { return false; } } }这里的关键点是tokenType字段。如果 Access Token 过期后有人拿着过期的 Access Token 去刷新接口后端不仅不应该刷新成功还应该拒绝。通过tokenType区分能防止这种误用。4.4 编写刷新接口刷新接口的核心逻辑是校验 Refresh Token 是否有效有效则签发新的 Access Token。package com.example.tokenrefresh.controller; import com.example.tokenrefresh.util.JwtUtil; import io.jsonwebtoken.Claims; import org.springframework.web.bind.annotation.*; import java.util.HashMap; import java.util.Map; RestController RequestMapping(/auth) public class AuthController { PostMapping(/refresh) public MapString, Object refreshToken(RequestHeader(refreshToken) String refreshToken) { MapString, Object result new HashMap(); // 1. 刷新 Token 必须使用 refreshToken 去换取 if (!JwtUtil.isRefreshToken(refreshToken)) { result.put(code, 40001); result.put(message, 非法的刷新令牌); return result; } // 2. 解析并判断是否过期 Claims claims; try { claims JwtUtil.parseToken(refreshToken); } catch (Exception e) { result.put(code, 40002); result.put(message, 刷新令牌已过期请重新登录); return result; } // 3. 重新生成 accessToken也可以同时刷新 refreshToken Long userId Long.valueOf(claims.get(userId).toString()); String username claims.get(username).toString(); String newAccessToken JwtUtil.generateAccessToken(userId, username); String newRefreshToken JwtUtil.generateRefreshToken(userId, username); result.put(code, 0); result.put(accessToken, newAccessToken); result.put(refreshToken, newRefreshToken); return result; } }说明这里的刷新逻辑为了方便演示是根据旧 Refresh Token 直接生成新的。生产环境通常还会做 Refresh Token 轮换也就是每次刷新后旧 Refresh Token 立即失效防止被重放攻击。4.5 编写登录接口登录成功时同时返回两个 TokenPostMapping(/login) public MapString, Object login(RequestBody MapString, String loginForm) { String username loginForm.get(username); String password loginForm.get(password); // 实际项目这里应该校验数据库用户信息 // 这里为了演示直接假设用户存在ID 为 1001 Long userId 1001L; MapString, Object result new HashMap(); result.put(code, 0); result.put(accessToken, JwtUtil.generateAccessToken(userId, username)); result.put(refreshToken, JwtUtil.generateRefreshToken(userId, username)); return result; }4.6 拦截器里如何判断过期拦截器这里只做“识别”如果 Access Token 过期就返回 401让前端去调用刷新接口。package com.example.tokenrefresh.interceptor; import com.example.tokenrefresh.util.JwtUtil; import org.springframework.stereotype.Component; import org.springframework.web.servlet.HandlerInterceptor; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; Component public class AuthInterceptor implements HandlerInterceptor { Override public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception { // 放行登录和刷新接口 String uri request.getRequestURI(); if (uri.contains(/auth/login) || uri.contains(/auth/refresh)) { return true; } String authHeader request.getHeader(Authorization); if (authHeader null || !authHeader.startsWith(Bearer )) { response.setStatus(401); response.setContentType(application/json;charsetUTF-8); response.getWriter().write({\code\:401,\message\:\token不存在\}); return false; } String token authHeader.substring(7); if (JwtUtil.isExpired(token)) { response.setStatus(401); response.setContentType(application/json;charsetUTF-8); response.getWriter().write({\code\:401,\message\:\token已过期\}); return false; } if (!JwtUtil.isAccessToken(token)) { response.setStatus(401); response.setContentType(application/json;charsetUTF-8); response.getWriter().write({\code\:401,\message\:\非访问令牌\}); return false; } return true; } }注册拦截器package com.example.tokenrefresh.config; import com.example.tokenrefresh.interceptor.AuthInterceptor; import org.springframework.context.annotation.Configuration; import org.springframework.web.servlet.config.annotation.InterceptorRegistry; import org.springframework.web.servlet.config.annotation.WebMvcConfigurer; Configuration public class WebConfig implements WebMvcConfigurer { private final AuthInterceptor authInterceptor; public WebConfig(AuthInterceptor authInterceptor) { this.authInterceptor authInterceptor; } Override public void addInterceptors(InterceptorRegistry registry) { registry.addInterceptor(authInterceptor) .addPathPatterns(/**); } }4.7 前端 Axios 拦截器自动续期测试人员如果自己搭接口自动化框架可以用 Python requests 或 Java HttpClient 写类似逻辑。这里以 Axios 为例这是目前前端主流的自动续期写法。// 文件路径src/utils/request.js import axios from axios; const request axios.create({ baseURL: https://api.example.com, timeout: 10000 }); // 标记是否正在刷新 Token let isRefreshing false; // 等待刷新期间把原请求暂存起来 let pendingRequests []; // 请求拦截自动携带 Token request.interceptors.request.use(config { const accessToken localStorage.getItem(accessToken); if (accessToken) { config.headers.Authorization Bearer accessToken; } return config; }); // 响应拦截统一处理 401 request.interceptors.response.use( response response.data, async error { const { response, config } error; if (!response) { return Promise.reject(error); } // 接口返回 401 if (response.status 401) { // 防止刷新接口自己无限递归 if (config.url.includes(/auth/refresh)) { redirectToLogin(); return Promise.reject(error); } const refreshToken localStorage.getItem(refreshToken); if (!refreshToken) { redirectToLogin(); return Promise.reject(error); } // 如果正在刷新先把请求加入队列 if (isRefreshing) { return new Promise((resolve, reject) { pendingRequests.push({ config, resolve, reject }); }); } isRefreshing true; try { // 用 refreshToken 换新 token const res await axios.post(/auth/refresh, null, { headers: { refreshToken: refreshToken } }); const { accessToken, refreshToken: newRefreshToken } res.data; localStorage.setItem(accessToken, accessToken); localStorage.setItem(refreshToken, newRefreshToken); // 重放等待队列中的请求 pendingRequests.forEach(item { item.config.headers.Authorization Bearer accessToken; request(item.config).then(item.resolve).catch(item.reject); }); pendingRequests []; // 重放当前请求 config.headers.Authorization Bearer accessToken; return request(config); } catch (refreshError) { redirectToLogin(); return Promise.reject(refreshError); } finally { isRefreshing false; } } return Promise.reject(error); } ); function redirectToLogin() { localStorage.removeItem(accessToken); localStorage.removeItem(refreshToken); window.location.href /login; } export default request;这段代码解决了一个非常经典的并发问题当多个接口同时返回 401 时不能每个接口都去调一次刷新接口否则会造成 Refresh Token 并发刷新旧 Token 失效导致全部失败。这里用isRefreshing标记把其他请求先挂起等刷新完成后再统一重放。4.8 运行和验证启动 Spring Boot 项目后可以按以下步骤验证调用登录接口curl -X POST http://localhost:8080/auth/login \ -H Content-Type: application/json \ -d {username:test,password:123456}拿到返回的 accessToken 和 refreshToken 后调用需要鉴权的接口curl -X GET http://localhost:8080/api/user/info \ -H Authorization: Bearer {accessToken}等 accessToken 过期后再用 refreshToken 调用刷新接口观察是否返回新的 accessToken。5. 高频追问并发刷新、Token 轮换与“记住我”5.1 多个请求同时 401 怎么办这是面试官非常喜欢的追问。如果前端同时发出 3 个请求3 个请求都因为 Access Token 过期返回 401可能会同时调用 3 次刷新接口。但 Refresh Token 如果做了轮换第一个刷新成功后就作废了后面两个刷新会失败最终用户被强制登录。解决办法就是前面 Axios 代码里展示的“请求队列 单一刷新标记”第一个 401 触发刷新逻辑。后面进来的 401 不发起刷新而是进入等待队列。刷新完成后用新 Token 重放等待队列里的所有请求。如果刷新失败所有等待请求一起失败并跳登录页。5.2 Token 轮换是什么Token 轮换的意思是每次使用 Refresh Token 刷新时不仅返回新的 Access Token还同时返回一个新的 Refresh Token并且服务端把旧 Refresh Token 标记为失效。这样做的好处是如果旧 Refresh Token 被窃取只能使用一次。服务端可以检测到 Refresh Token 异常重用。更安全但实现上需要把 Refresh Token 存储到 Redis 或者数据库不能只靠 JWT 无状态解析。5.3 刷新 Refresh Token 时谁去刷新我们上面的实现里/auth/refresh接口同时返回了新的 accessToken 和 refreshToken。这是推荐做法。另一种做法是刷新接口只返回新的 accessTokenrefreshToken 一直不变直到它自身的有效期到期。这种方式实现简单但安全性稍差适合内部系统或低风险场景。5.4 “记住我”功能如何实现很多网站会有“记住我”复选项勾选后一周内不用重新登录不勾选则关闭浏览器就退出。实现思路一般是未勾选Refresh Token 有效期设为较短比如 12 小时并存放在 Cookie 或内存中。勾选Refresh Token 有效期设为 7 天或 30 天并持久化到 localStorage 或数据库。测试人员设计测试用例时可以关注这样几个点勾选和不勾选Token 的有效期是否有区别。关闭浏览器后重新打开是否还能自动登录。修改密码后旧的 Refresh Token 是否失效。在多台设备登录是否允许多人同时在线。5.5 服务端主动下线如何实现JWT 无状态服务端不能直接把 JWT 作废。如果业务需要“用户改密码后强制下线”通常用两个方案在 Redis 里存一个“用户 Token 版本号”每次改密码 1。JWT 解析通过后再对比版本号。把 JWT 的 jtiJWT ID存入 Redis 黑名单过期时间等于剩余有效期。这也解释了为什么很多系统实际使用的是“JWT Redis 组合”而非纯无状态 JWT。6. 常见问题排查Token 失效相关的报错案例实际开发中Token 相关报错种类很多。下面整理几个典型场景尤其结合项目里常见的接口对接问题。6.1 接口请求返回 401 Unauthorized可能原因排查思路Token 过期查看返回体中的错误码是否为 token_expiredToken 被服务端主动拉黑检查用户状态、密码是否修改、后台是否踢人Token 类型不对是否将 Access Token 传给了刷新接口请求头格式不对检查是否漏掉了Bearer前缀服务端公钥或签名密钥不一致多环境部署时容易踩坑检查配置时间不同步JWT 的iat、exp依赖服务器时间服务器时间不准会导致误判过期6.2 调用第三方 API 报 token exchange failed这类报错常见于 OAuth2/OIDC 授权码交换阶段或者某些平台间的 Token 交换接口。我们经常在对接第三方平台时看到类似sign-in could not be completed token exchange failed token endpoint returned status 403 forbidden这种错误通常不是一个点而是多个环节都可能触发排查顺序建议如下排查步骤检查内容检查授权服务器配置确认 client_id 和 client_secret 是否正确回调地址是否白名单检查网络策略服务器出口 IP 是否在授权服务器允许范围内检查请求参数grant_type 是否写成了authorization_codecode 是否过期检查账号权限当前账号在该租户或组织下是否有对应权限检查地区限制部分平台会根据 IP 或账号归属地限制 Token 交换查看平台状态确认对应 API 服务是否处于维护或故障状态这里要特别强调一点遇到 403 时不要只盯着客户端代码还要检查服务端策略、IP 白名单、地区限制等。尤其是企业内网对接外部平台时出口 IP 变化是最常见的坑。6.3 刷新 Token 后原请求重放失败如果实现了“401 后自动刷新再重放”但重放仍然失败通常原因有刷新成功后未及时更新内存中的 token重放请求仍然携带旧 Token。部分接口对幂等性要求较高重复提交产生冲突。比如支付接口、创建订单接口重放时可能返回“订单已存在”。接口参数中绑定了旧 Token 的信息比如签名串里包含旧 Token。解决建议自动化测试框架里对 GET 等幂等接口可以做自动重放对 POST 等非幂等接口则要根据业务决定是否重放。另外在做接口自动化时最好在测试前置步骤中先确认 Token 有效而不是等跑到中途再刷新。7. Token 设计的最佳实践与工程建议7.1 测试工程师眼中的 Token 测试点如果你需要为公司设计 Token 相关测试用例建议覆盖以下场景正常情况下获取 Token、携带 Token 访问接口。不携带 Token、携带非法 Token、携带过期 Token。Token 有效性边界测试正好在过期时间点前后访问。并发请求时 Token 自动续期是否正常。刷新 Token 后旧 Token 是否立即失效。多设备登录、单端登录、强制下线。Token 在 Redis 中的存储时间与服务端配置时间是否一致。接口超时、重试、幂等设计是否合理。不同环境测试环境、预发环境的 Token 密钥是否隔离。7.2 安全层面建议Token 不要放在 URL 参数中优先使用Authorization: Bearer请求头。Token 不要明文传输生产环境必须启用 HTTPS。Refresh Token 过期时间尽量比 Access Token 长但不能过长。不要在日志中打印 Token 明文。JWT 的密钥长度必须足够推荐使用 HS256 时密钥至少 256 位。生产环境密钥不要提交到代码仓库使用环境变量或配置中心管理。如果用户投诉“自己的账号被异地登录”设计时最好能记录 Token 签发设备和 IP。7.3 性能层面建议JWT 本身无状态适合分布式系统但要避免把大量用户信息塞进 Payload。每次请求都解析 JWT 有 CPU 开销高并发场景下可以使用本地缓存或网关层统一鉴权。Redis 存储 Token 时要给 key 设置合理的 TTL避免 Redis 内存被无用的过期 Token 占满。刷新 Token 接口要限流防止恶意刷新。8. 写在最后的面试回答参考如果你正在准备软件测试面试可以按下面这个框架组织回答既有原理又有实战听起来会更完整。先说结论Token 是否过期可以从本地解析和服务端校验两个层面判断。如果用的是 JWT本地解析 Payload 里的exp字段最方便但服务端可能因为用户状态变化提前让 Token 失效所以更稳妥的是服务端结合用户状态校验。然后说自动更新一般采用双 Token 机制Access Token 短期有效Refresh Token 长期有效。前端拦截所有接口返回的 401当发现 Access Token 过期时先尝试用 Refresh Token 调用刷新接口拿到新 Token 后重放原请求避免用户无感知地退出登录。设计时需要关注并发刷新问题多个 401 同时到达时只发一次刷新请求其他请求排队等待。最后加一句自己在项目中的实践我在接口自动化框架里做过一个统一的 Token 管理模块登录后把 Token 放到全局变量所有用例自动携带用例执行过程中如果收到 401会自动刷新并重放。这样既保证了用例稳定性也把 Token 的过期处理变成了一次可复用的能力。如果聊到这里面试官基本可以确认你对 Token 的生命周期、刷新策略、并发处理都有比较完整的认知这道题也就过关了。如果你正在做测试框架建议不要只停留在“会调接口”的层面而是尝试在自己的框架中实现一个 Token 自动续期模块。真正把这个流程跑通之后你对登录态、会话管理、前端拦截器这些概念的理解会明显上一个台阶后续不管是接口测试、自动化测试还是性能测试都多了一项很实用的底层能力。
返回列表