在API测试和开发过程中,CSRF(跨站请求伪造)防护机制是常见的安全措施。服务器会生成一个唯一的令牌(Token),要求客户端在后续请求中携带这个令牌,以验证请求的合法性。传统的手动处理方式需要开发者:
这种方式不仅效率低下,还容易出错。特别是在以下场景中问题更加突出:
我曾经在一个电商项目上踩过坑:每次下单测试都要手动复制粘贴6个不同的Header值,一个下午下来手腕都酸了。后来发现用Postman的脚本功能可以完全自动化这个过程,效率提升了至少10倍。
Postman提供了强大的预请求脚本(Pre-request Script)和测试脚本(Tests)功能,配合环境变量(Environment Variables),可以实现动态的令牌管理。整个自动化流程包含三个关键组件:
环境变量相当于全局的键值存储,可以在不同请求间共享数据。我们主要需要两个变量:
XCSRFToken:存储从服务器获取的最新CSRF令牌Cookie:存储拼接好的Cookie字符串这些变量的生命周期可以设置为:
在获取令牌的GET请求的Tests脚本中,我们可以编写JavaScript代码来解析响应:
javascript复制// 从响应头获取CSRF令牌
const csrfToken = pm.response.headers.get('x-csrf-token');
pm.environment.set('XCSRFToken', csrfToken);
// 收集所有Set-Cookie头并拼接
const cookies = pm.response.headers
.filter(header => header.key === 'Set-Cookie')
.map(header => header.value)
.join(';');
pm.environment.set('Cookie', cookies);
这段代码会自动提取所需的认证信息并存入环境变量。
在需要认证的POST请求中,我们不需要手动设置Header,而是使用变量占位符:
x-csrf-token值设为{{XCSRFToken}}{{Cookie}}Postman会在发送请求前自动用环境变量的值替换这些占位符。
下面我带大家一步步实现这个自动化流程,以测试一个需要CSRF保护的REST API为例。
首先创建一个新的环境配置:
XCSRFToken和Cookie,初始值留空切换到新建的环境,确保后续操作都在这个环境下进行。
新建一个GET请求,指向获取令牌的端点(通常是/login或/session):
http复制GET https://api.example.com/session
Headers:
Accept: application/json
在Tests标签页中添加令牌捕获脚本(就是上面2.2节的代码)。发送这个请求后,检查环境变量是否已经正确更新。
新建一个需要CSRF保护的POST请求:
http复制POST https://api.example.com/orders
Headers:
x-csrf-token: {{XCSRFToken}}
Cookie: {{Cookie}}
Content-Type: application/json
Body:
{
"product_id": 123,
"quantity": 2
}
现在直接发送这个POST请求,Postman会自动注入当前的令牌和Cookie值。
将这两个请求组织到一个集合中:
javascript复制// 确保变量存在
if (!pm.environment.has('XCSRFToken')) {
pm.environment.set('XCSRFToken', '');
}
if (!pm.environment.has('Cookie')) {
pm.environment.set('Cookie', '');
}
这样就能确保集合运行时变量总是可用的。
实现基础功能后,我们可以进一步优化这个自动化流程。
很多CSRF令牌都有有效期,我们可以通过以下方式处理:
javascript复制pm.environment.set('TokenTimestamp', new Date().getTime());
javascript复制const tokenAge = new Date().getTime() - pm.environment.get('TokenTimestamp');
if (tokenAge > 300000) { // 5分钟过期
pm.sendRequest({
url: 'https://api.example.com/session',
method: 'GET'
}, (err, res) => {
// 更新令牌逻辑
});
}
增强脚本的健壮性:
javascript复制if (pm.response.code !== 200) {
pm.environment.unset('XCSRFToken');
throw new Error('获取令牌失败');
}
javascript复制const maxRetries = 3;
let retryCount = 0;
function getToken() {
pm.sendRequest({
url: 'https://api.example.com/session',
method: 'GET'
}, (err, res) => {
if (err || !res.json().token) {
if (retryCount < maxRetries) {
retryCount++;
getToken();
}
}
// 正常处理
});
}
对于不同环境(开发/测试/生产),我们可以:
javascript复制const env = pm.environment.name;
const baseUrl = {
'Dev': 'https://dev.api.example.com',
'Prod': 'https://api.example.com'
}[env] || 'https://test.api.example.com';
pm.environment.set('BaseUrl', baseUrl);
然后在请求URL中使用{{BaseUrl}}/session这样的变量。
在实际使用中可能会遇到以下问题:
如果收到"CSRF token validation failed"错误,检查:
当遇到Cookie相关错误时:
如果变量似乎没有正确更新:
我曾在项目中遇到一个棘手问题:脚本看似执行成功但变量没更新。最后发现是因为服务器返回的Set-Cookie头名称大小写不一致,导致过滤失败。添加了大小写不敏感的匹配才解决:
javascript复制if (aHeaders[i].key.toLowerCase() === 'set-cookie') {
aCookies.push(aHeaders[i].value)
}
当需要高频使用CSRF令牌时,可以考虑以下优化:
一个实用的技巧是使用Postman的setNextRequest功能创建灵活的流程:
javascript复制// 在获取令牌请求的Tests中
if (pm.response.code === 200) {
postman.setNextRequest("Create Order");
} else {
postman.setNextRequest(null); // 停止执行
}
这样可以在令牌获取成功后才执行后续请求。