ARTICLE DETAIL

资讯详情

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

Postman接口测试鉴权全解:Cookie、Session、Token实战与401排查指南

Postman接口测试鉴权全解:Cookie、Session、Token实战与401排查指南 做接口测试的十个里有九个在鉴权上翻过车。我刚入行那会儿测一个带登录态的接口明明在浏览器里点得通搬到Postman里就死活返回401后来才知道是Cookie没带对。Postman接口测试里Cookie、Token、Session这三样东西名字都听过但它们的存储位置、验证方式、失效逻辑完全不一样而Postman对它们的处理也各有各的坑。这篇文章我按自己的实战经验把三者在Postman里的测试方法、自动化套路、以及常见的报错排查思路完整写一遍给刚接触接口测试的新手也给那些想系统理一理鉴权逻辑的开发者希望能少走点弯路。1. 从三者的本质区别开始Cookie、Session、Token到底在验证什么1.1 HTTP为什么需要一个身份凭证HTTP协议本身是无状态的。什么意思就是你每次发一个请求服务器都当作一个全新的陌生人来看待它不记得你上次是不是登录过。这就像你去一家不记人的咖啡店每次进门店员都问您要喝什么哪怕你十分钟前刚点过一杯。无状态是HTTP设计之初为了简单和可靠刻意做的取舍但现代应用需要记住用户是谁于是鉴权机制就出现了。鉴权的本质就是让客户端在每次请求中都能携带一个我是谁、我有什么权限的证明服务端拿到这个证明之后进行校验校验通过才放行。这个证明用的就是Cookie、Session、Token这三套方案。做接口测试的时候搞清楚系统用的是哪一套方案比记住100个Postman快捷键都重要因为你后面所有的请求设计、脚本编写都是围绕这套方案展开的。1.2 三种机制的底层逻辑拆解Cookie的玩法是用户登录成功之后服务端通过Set-Cookie响应头把一段键值对比如uid123456发给浏览器浏览器负责把它存下来之后每次访问同一个域名浏览器自动在请求头里带上Cookie。Cookie在中文里经常被调侃成小饼干但它本质就是存储在客户端的小纸条服务端让你带你就带着全靠自觉。Session的玩法是服务端在内存或者Redis里建一份用户状态的记录同时生成一个唯一的session id把这个id放在Cookie里交给客户端。客户端每次请求带上这个id服务端拿id去自己的存储里找到对应的用户信息。所以Session的关键在于服务端认账——它自己存了一份客户端只是把钥匙带回来了。用酒店前台的比喻比较好理解前台服务端登记了你的信息并发给你房卡session id你每次进酒店出示房卡前台一查登记本就知道你是谁。Token的玩法又不一样它把用户信息直接打包进凭证里用签名防篡改服务端不存储任何会话状态。最典型的是JWT分为三段header声明加密算法、payload带用户ID、过期时间等、signature服务端私钥签名。客户端拿到token后每次请求放在Authorization头里服务端验签通过就认。Token理解为带防伪标识的盖章票据最直观——票据本身写着你是谁盖章证明这东西是真的谁检票都能验证票务中心服务端不用专门记你的名字。1.3 一张表看清三者的核心差异用一个对比表格可以很快抓住重点对比项CookieSessionToken凭证存储位置客户端服务端内存/Redis/DB客户端服务端是否存状态不额外存储必须存储不存储凭证是否可被服务端主动吊销只能靠过期时间间接控制可以删除session即可较难只能等过期或用黑名单典型应用场景传统Web登录态传统Web登录态、有状态服务前后端分离、移动端、开放API传输方式Cookie请求头Cookie中携带session idAuthorization请求头常见补充一点Cookie和Session并不是对立关系实际项目里经常是Session借助Cookie来传输session id两者配合使用。所以你在Postman里测session类接口本质还是在操作Cookie这一点后面会展开说。2. Postman中的Cookie实际操作从浏览器迁过来以及最容易翻车的细节2.1 Postman的Cookie管理机制别被自动行为坑了Postman本身内置了一套Cookie管理功能也就是常说的Cookie JarCookie罐子。当你用Postman发送请求如果响应头里有Set-CookiePostman会自动把它存入当前域名的Cookie缓存里后续再请求同一域名的接口时会自动带上。这个设计本意是帮你省事但也有不少人被它坑过。最典型的坑是你自己在请求的Headers里手动加了一条Cookie但请求发出去后服务端收到的Cookie不是你填的那份。因为Postman会在发送时自动合并该域名Cookie Jar里的内容如果Jar里已经有同名的Cookie自动管理的值可能会覆盖你手写的那份或者两者同时存在造成冲突。排查方法很简单把Postman的Console打开View - Show Postman Console看实际发出的请求头长什么样一切都清楚了。如果你要完全手动控制Cookie最干净的办法是把Jar里对应的Cookie清掉或者到Postman的Settings里关闭自动Cookie处理然后在Headers里写死Cookie字符串。不过我的经验是日常测试开自动管理更方便手动写Cookie的场景多见于排查问题和模拟异常不要反过来用。2.2 从浏览器迁移Cookie的正确姿势接口联调的时候最常见的操作是把浏览器里的登录态搬到Postman。做法分三步在浏览器里按F12打开开发者工具切到Network面板随便点一个请求在右侧Request Headers里找到Cookie那一行整体复制。回Postman在请求的Headers页签里新增一行key填Cookievalue填你复制的一整串。发请求试一下如果服务端返回200说明登录态已经带上了。这里有一个细节很多人忽略Cookie字符串是有域名的浏览器里你访问的是A域名复制出来的Cookie只能用于A域名或者A域名的子域。你在Postman里如果用了IP访问、或者本机改了host转发到别的域名Cookie不会生效。Cookie的匹配规则还包括Path服务端返回的Set-Cookie如果指定了path/api那么请求/api以外的接口时Cookie也不会带上。这些规则看似基础但在实际联调中导致的明明复制了Cookie却401的情况我见过太多次了。另外关于Cookie的查看网上很多人搜夸克网盘cookie在哪里查看360浏览器怎么导出cookie之类的问题本质都是一回事打开开发者工具在Application或者Network面板里面找。个别浏览器有扩展插件能一键导出Netscape格式的Cookie文件但Postman其实用不到那么复杂直接在Network面板复制字符串就够了。别为了一个Cookie字符串去装一堆来路不明的插件没必要。2.3 Cookie过期和假登录问题Cookie不是永久的服务端在Set-Cookie时可以指定Expires或Max-Age。测试中最常见的情况是你上午复制了一个Cookie下午打开Postman继续跑接口突然开始报401。第一反应别急着怀疑代码先看看是不是Cookie过期了。更隐蔽的假登录问题是这样的有些前端页面登录之后服务端返回的登录态Cookie是HttpOnly的JS读不到但页面能正常访问数据。你把Cookie复制出来在Postman里发请求发现也是通的。可一旦你新起一个浏览器窗口重新登录旧Cookie可能已经完全换掉了。如果测试脚本里写死了Cookie你的接口测试就会变得极度脆弱。解决方案是把获取Cookie这个动作也做成接口测试的依赖步骤先用Postman跑一遍登录接口拿Set-Cookie再跑后续业务接口。这样每次执行都是新鲜登录态比手动粘贴靠谱得多。我的建议是Cookie类的接口测试把登录请求放在同一个Collection里的第一个请求后面的请求依靠Postman的Cookie Jar自动带上。只要域名、路径匹配这一套跑起来非常顺。至于把Cookie写死在环境变量里的做法只适合调试临时用不推荐进回归脚本。3. Token自动化的完整套路从手动粘贴到脚本自动保存和刷新3.1 最原始的Token测试方式复制粘贴Token鉴权的测试绝大多数新手都是从复制粘贴开始的。登录接口返回体里通常长这样{ code: 0, data: { token: eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.xxx, tokenType: Bearer } }你把token复制出来然后在新请求的Headers里加一行Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.xxx这种方式在单个调试场景下没问题但Token的有效期通常不长很多系统是2小时甚至更短过期之后你又得重新登录、重新复制、重新粘贴。如果同一时间要跑一二十个接口这个体力活就很折磨人。更危险的是粘贴的时候容易复制不全复制到最后漏了几个字符请求就开始报Invalid token你还百思不得其解。所以我一直主张只要Token逻辑需要重复操作两次以上就花两分钟把它自动化一劳永逸。3.2 用Tests脚本自动保存TokenPostman的每个请求都有两个脚本执行阶段Pre-request Script请求发送前执行和Tests请求发送后执行。我们拿到token的时机是请求后所以用Tests保存token最合适。在登录接口的Tests页签里写const res pm.response.json(); if (pm.response.code 200 res.code 0 res.data res.data.token) { pm.environment.set(accessToken, res.data.token); pm.environment.set(tokenType, res.data.tokenType || Bearer); }保存之后后续请求的Headers里这样引用Authorization: {{tokenType}} {{accessToken}}这里有两个细节要提醒。第一保存前一定要做完整的响应校验别只判断HTTP 200。很多系统登录失败时也返回200只是业务状态码变了如果你不加校验就把一个错误信息存进环境变量后面的请求会全部失败。第二环境变量名要起得清晰多个接口共用时别东一个名西一个名建议统一用accessToken、refreshToken、tokenExpire这种约定。3.3 用前置脚本实现Token自动续签Token有效期短的系统通常会配套提供Refresh Token机制。登录时返回两个tokenaccessToken短期有效和refreshToken长期有效。accessToken过期后客户端拿refreshToken去换一个新的accessToken。在Postman里我们可以这样设计把刷新Token的接口也写成一个请求但更推荐的做法是用Pre-request Script做自动续签。思路是在脚本里判断当前accessToken是否即将过期如果快过期就自动调用刷新接口拿新token再继续发送原请求。脚本大致长这样const exp pm.environment.get(tokenExpire); const baseUrl pm.environment.get(baseUrl); const refreshToken pm.environment.get(refreshToken); function saveToken(res) { if (res.code 200 res.data res.data.accessToken) { pm.environment.set(accessToken, res.data.accessToken); pm.environment.set(tokenExpire, Date.now() (res.data.expiresIn - 60) * 1000); } } if (!exp || Date.now() exp) { const req { url: baseUrl /auth/refresh, method: POST, header: { Content-Type: application/json }, body: { mode: raw, raw: JSON.stringify({ refreshToken: refreshToken }) } }; pm.sendRequest(req, function(err, res) { if (!err) { saveToken(res); } else { console.log(Token刷新失败, err); } }); }这里最关键的一个点是pm.sendRequest是异步执行的。它不会阻塞主请求的发送也就是说如果脚本里直接调pm.sendRequest发刷新请求主请求可能已经带着旧token发出去了你设置的环境变量还没生效。要严格保证先刷新再继续可以在刷新请求的回调里不主动做什么然后让后续请求重新尝试或者干脆把刷新请求单独做成一个请求按顺序放在登录之后的固定位置用Collection级别的流程控制来排队执行。我实测下来单独刷新请求按顺序执行的稳定性要好于依赖异步脚本钩子。如果你不想搞太复杂还有一个折中的办法给accessToken单独建一个环境变量做统一管理在Collection的Pre-request Script里统一检查并刷新。总体思路是统一入口、统一判断不要把刷新逻辑散落在几十个请求里。3.4 环境变量作用域与多环境切换Token自动化必然牵扯环境变量的作用域。Postman的变量从大到小分四层Global全局、Environment环境、Collection集合、Data数据文件。取值优先级是Data Collection Environment Global也就是说越具体的变量优先级越高。开发、测试、生产三套环境切换的时候我的习惯是每套环境都维护一份独立的accessToken、refreshToken、baseUrl变量。注意Git这类代码托管平台上千万不要把真实环境的token提交上去token是敏感信息一旦泄露等于把线上接口的登录态送出去了。建议做法是环境文件里用占位符CI里通过加密的Secret注入或者用Postman自带的敏感变量功能。这一块很多人不重视接口测试脚本里直接写死token最后仓库泄露导致线上被刷的案例真的不少。多环境还有一个常见的坑token是绑定环境和账号的你在dev环境登录拿的token切到test环境去用一定失败。所以跑自动化用例之前第一步永远是保证当前环境变量里的token和当前环境的账号匹配。我会在Collection的Pre-request Script里加一道检查不需要多复杂判断一下baseUrl和token的匹配关系就行。4. Session会话保持登录成功却拿不到数据的真相4.1 Session在Postman里的会话保持逻辑Session类接口的测试很多人以为要手动处理session id其实Postman的Cookie Jar已经在帮你干了。原理很简单服务端登录成功后返回Set-Cookie里面带着session idPostman自动存进Cookie Jar下一次请求同一域名自动带上这个Cookie服务端根据session id找回用户状态。所以它的正确测试姿势和Cookie是一样的先把登录接口跑成功再跑业务接口。理论上这个流程很简单但实际操作中登录成功却拿不到数据的现象非常普遍。我在公司里帮别人排查过不下二十次这个问题原因翻来覆去就那么几个先说最常见的。4.2 手写Cookie和自动Cookie冲突的经典翻车场景我见过最多的场景是测试A登录成功之后把响应里的Set-Cookie或者浏览器里看到的session id复制了出来手动加到Headers里。过了一段时间session过期了他又重新登录了一次Postman的Cookie Jar里已经换成了新的session id但他Headers里手写的那份还是旧的。问题来了Postman发送请求时Cookie Jar自动带上了新的session idHeaders里手写的旧session id也还在服务端收到的Cookie字符串可能同时包含两个值。不同后端框架对重复Cookie的解析规则不一样有的取第一个有的取最后一个有的直接报错——于是你看到的结果就是偶尔成功偶尔401非常难以捉摸。这个坑的解法很朴素手动写Cookie的时候先到Postman的Cookies管理器里把该域名的自动Cookie清空确保请求里只有你手写的那一份。排查的时候打开Console看实际的请求头里Cookie的具体值一眼就能看出是不是重复了。4.3 Session过期、服务端重启与并发干扰Session类接口还有三个高频问题都是环境因素而非代码逻辑问题。第一是Session过期。服务端一般会给Session设置空闲超时时间比如30分钟没有请求就自动失效。测试过程中如果中间停顿了一段时间再发请求就直接被判定未登录。这不是bug是设计如此重新跑登录流程即可。第二是服务端重启或Session存储清空。如果用内存存Session服务端一重启所有session id全部失效。如果你用Runner批量跑测试中途服务端重启了后面的用例会成片失败。这种情况下优先建议后端把Session存储切到Redis这类独立组件如果切不了测试就要接受重启后必须重新登录的现实并且在脚本里做登录态失败自动重登的处理。第三是并发干扰。用Postman Runner并发跑用例时多个迭代共享同一个Cookie JarSession状态会互相覆盖。比如迭代1登录了用户A迭代2登录了用户B因为jar是共享的最后一次登录的session id会覆盖前面的迭代1后续请求可能用的是用户B的登录态。这会导致测试结果完全失真。要模拟多用户并发Session场景正确做法是不要依赖自动Cookie Jar而是每个迭代用不同的环境变量或者数据文件传session id在请求头里手动指定Cookie确保每个用户一套独立凭证。关于Session的异常测试还有一点想多说有经验的测试人员会验证服务端对无效session id的处理是否正确——传一个不存在的session id服务端应该干脆地返回401或者重定向到登录页而不是抛一个HTTP 500或者直接崩溃。这个验证在Postman里做很简单把Cookie值改成一个无效字符串发请求就行。它不涉及什么复杂的攻击技术单纯是检验服务端对异常输入的健壮性这个用例放回归里是非常有价值的。5. 鉴权失败排查手册从报错反推问题根源5.1 先分清401和403接口报错的时候先看状态码这一步能过滤掉一大半问题。401 Unauthorized意思是你没有凭证或者凭证不对比如没带Authorization头、token过期、签名不匹配、cookie里没有session id。这属于认证层问题排查重点是凭证有没有带上、带上的对不对。403 Forbidden意思是凭证有效但你没有权限访问这个资源比如普通用户访问管理员接口、用户A访问用户B的数据、IP白名单限制。这属于授权层问题排查重点是当前账号的角色/权限是不是够。这两者经常被混为一谈我见过有人为了修403反复去改token改了半天其实问题出在账号权限上。所以看到403第一件事是换个有权限的账号试而不是换凭证。5.2 高频鉴权报错逐条拆解我把实际工作中遇到最多的鉴权报错整理成一张表每条给出根因和排查方向报错/现象可能根因排查方向401 响应提示Signature has expiredToken过期或本机系统时间不准检查系统时间重新登录拿新token401 提示Invalid tokenToken被截断、带了空格换行、Bearer前缀写错重新复制完整token看Console实际发出的Authorization值401 提示Session not found服务端Session已失效或从未创建重新登录确认session id是否被正确携带登录成功但列表接口403账号权限不足换管理员账号验证检查用户角色配置OAuth2报Token exchange failed授权码过期、redirect_uri不匹配、client_secret错误核对OAuth2配置确认授权码有效期Header里有token但服务端看不到变量名带出空格、{{token}}未渲染悬停变量名看渲染值确认环境变量存在部分接口通、部分接口401Cookie的path/domain限制导致部分请求没带Cookie看Set-Cookie的path配置调整请求路径或Cookie设置服务端报Could not open sessionHibernate数据库会话连接异常不是用户登录态让后端查数据库连接池配置这里特别提一下最后一行的Hibernate报错。很多人在排查登录态问题的时候看到session两个字就以为是会话过期其实Could not open hibernate session for transaction里的session是数据库会话ORM框架的Session跟用户登录态完全是两回事。这个报错通常指向数据库连接问题比如连接池打满、数据库宕机、事务配置错误。遇到这种情况别再折腾Postman了赶紧让后端工程师看日志。另外OAuth2的Token exchange失败在联调中也很常见。它发生在用授权码换token这一步常见原因是授权码一次性使用且有效期极短有的只有几十秒你从浏览器里复制到Postman再粘贴的时候已经过期了。其次就是redirect_uri不一致授权服务器校验这个参数必须和申请时的回调地址完全一致。排查时把这三个参数逐项核对grant_type、redirect_uri、client_secret。5.3 一套能复用的排查流程排查鉴权问题我习惯按下面这套顺序走稳定且不会漏环节打开Postman ConsoleView - Show Postman Console先看请求实际发出的Header。这里能看到变量是否渲染成功、Cookie是否自动带上、Authorization的值是否完整。很多问题到这一步就直接水落石出了。确认凭证行为检查Authorization头里有没有Bearer前缀Cookie头里有没有多余的换行或空格特别是从浏览器复制过来的内容经常带上隐藏字符。确认凭证内容把token解析一下看payload里的过期时间exp、用户ID、签发时间iat是否符合预期。注意在线解析token的网页不要把真实token贴进去敏感环境请用本地的解析库或者拿脱敏后的token。确认环境变量悬停引用变量的地方Postman会显示渲染后的实际值确认当前环境是不是选对了dev/test/prod。单独隔离验证先用最简单的请求比如一个登录接口确认凭证流程是通的再逐步加上业务参数避免多个变量搅在一起。查服务端日志这一步很多时候比看响应体更有用让后端查一下这个请求到底有没有走到鉴权过滤器、鉴权过滤器里拦截在哪一环。这套流程大概十分钟之内能定位大部分鉴权问题。核心思路就一句话把凭证的产生、传递、校验三个环节分别验证哪一环断了就去哪一环修。最后说点个人体会。鉴权测试这东西表面上是Postman的操作技巧本质上考验的是你对凭证是怎么产生、怎么传递、怎么校验的理解。Cookie、Session、Token三种机制背后的设计取舍不同决定了它们在Postman里的处理方式也不同Cookie靠Cookie Jar自动管理Session本质是Cookie加服务端状态Token则要结合脚本做保存和续签。我做接口测试这几年返工最多的不是请求写错而是没搞清楚当前系统到底认什么、凭证在哪里失效、环境的切换有没有带来变量污染。把这些基础理顺了大部分鉴权问题都能在五分钟内定位剩下的时间花在真正有价值的测试用例设计上才是做接口测试该有的状态。
返回列表