1. LoadRunner认证失败问题解析
当你在LoadRunner中遇到{"msg":"请求访问:/getInfo,认证失败,无法访问系统资源","code":401}这样的错误时,这通常表示你的脚本在执行过程中遇到了认证问题。401状态码在HTTP协议中代表"Unauthorized",即未经授权的访问。
1.1 401错误的常见原因
在性能测试场景中,401错误通常由以下几种情况导致:
Token缺失或过期:现代Web应用普遍采用Token-based认证机制,如果你的脚本中没有正确获取或传递Token,就会触发401错误。
认证信息配置错误:在录制脚本时捕获的认证凭据可能已过期,或者在回放时没有正确更新。
会话超时:某些系统的会话有效期较短,在长时间的性能测试中可能导致会话过期。
CORS(跨域)问题:虽然CORS问题通常会返回403状态码,但在某些配置下也可能表现为401错误。
API密钥无效:如果目标系统使用API密钥进行认证,而你的脚本中配置的密钥不正确或已撤销。
2. LoadRunner脚本中的认证机制
2.1 Token-based认证处理
现代Web应用最常见的认证方式是JWT(JSON Web Token)或OAuth Token。在LoadRunner中处理这类认证需要以下步骤:
- 获取Token:通常通过登录接口获取
web_reg_save_param_ex( "ParamName=access_token", "LB=\"access_token\":\"", "RB=\"", SEARCH_FILTERS, LAST); web_submit_data("login", "Action=https://api.example.com/auth/login", "Method=POST", "EncType=application/json", "Body={\"username\":\"{username}\",\"password\":\"{password}\"}", LAST);- 在后续请求中使用Token:
web_add_header("Authorization", "Bearer {access_token}"); web_custom_request("get_info", "URL=https://api.example.com/getInfo", "Method=GET", LAST);2.2 基本认证(Basic Auth)处理
对于使用Basic Auth的系统,LoadRunner提供了专门的函数:
web_set_user("username", "password", "example.com:443");或者在请求头中直接添加:
char auth[100]; lr_save_string(lr_eval_string("{username}:{password}"), "credentials"); web_convert_param("credentials", "ENCODING", "BASE64", LAST); web_add_header("Authorization", "Basic {credentials}");3. 常见问题排查与解决方案
3.1 Token过期问题
症状:脚本刚开始运行正常,但一段时间后开始出现401错误。
解决方案:
- 在脚本中添加Token刷新逻辑
- 设置合理的Think Time模拟真实用户行为
- 检查服务器设置的Token过期时间,适当调整测试场景持续时间
// Token刷新示例 if (strstr(lr_eval_string("{response}"), "token_expired")) { web_reg_save_param_ex("ParamName=new_token", ...); web_submit_data("refresh_token", ...); lr_save_string(lr_eval_string("{new_token}"), "access_token"); }3.2 认证信息未正确传递
症状:所有请求都返回401错误。
排查步骤:
- 检查是否在所有需要认证的请求前添加了认证头
- 使用LoadRunner的日志功能验证Token是否正确生成和传递
- 对比录制脚本和实际请求的差异
// 调试输出Token内容 lr_output_message("使用的Token是: %s", lr_eval_string("{access_token}"));3.3 动态认证参数处理
很多系统会在登录时返回动态的CSRF Token或类似的防伪参数,这些也需要捕获并在后续请求中使用:
web_reg_save_param_ex("ParamName=csrf_token", "LB=<input type=\"hidden\" name=\"csrf_token\" value=\"", "RB=\"", LAST); web_submit_form("login", ITEMDATA, "Name=username", "Value={username}", ENDITEM, "Name=password", "Value={password}", ENDITEM, "Name=csrf_token", "Value={csrf_token}", ENDITEM, LAST);4. 高级认证场景处理
4.1 OAuth 2.0流程实现
对于使用OAuth 2.0的系统,需要在脚本中实现完整的授权码流程:
- 获取授权码
- 用授权码交换访问令牌
- 使用访问令牌访问资源
- 必要时刷新令牌
// 获取授权码 web_add_auto_header("Referer", "{referer_url}"); web_url("authorize", "URL=https://auth.example.com/authorize?response_type=code&client_id={client_id}&redirect_uri={redirect_uri}&scope={scope}", "Resource=0", "RecContentType=text/html", LAST); // 从重定向URL中提取code web_reg_save_param_ex("ParamName=auth_code", "LB=code=", "RB=&", LAST); // 交换Token web_submit_data("get_token", "Action=https://auth.example.com/token", "Method=POST", "EncType=application/x-www-form-urlencoded", "Body=grant_type=authorization_code&code={auth_code}&redirect_uri={redirect_uri}&client_id={client_id}&client_secret={client_secret}", LAST);4.2 证书认证处理
对于需要客户端证书的HTTPS服务,需要在运行时设置证书:
web_set_certificate_ex("CertFilePath=client.pem", "CertFormat=PEM", "KeyFilePath=key.pem", "Password=123456", LAST);5. 性能测试中的认证优化
在长时间运行的性能测试场景中,认证处理需要考虑以下优化:
- Token缓存:避免每次迭代都重新获取Token
- 连接复用:保持HTTP连接活跃减少握手开销
- 合理的认证频率:根据业务场景设计认证频率
// 使用lr_user_data_point实现Token缓存 if (lr_user_data_get("cached_token") == NULL) { // 获取新Token get_new_token(); lr_user_data_put("cached_token", lr_eval_string("{access_token}")); } else { lr_save_string(lr_user_data_get("cached_token"), "access_token"); }6. 调试技巧与工具
6.1 使用Fiddler辅助调试
- 配置LoadRunner使用Fiddler作为代理
web_set_proxy("localhost:8888");- 在Fiddler中检查实际发送的请求和接收的响应
- 对比成功和失败的请求差异
6.2 LoadRunner日志分析
启用详细日志记录:
web_set_option("Log", "ALL", LAST);在日志中搜索:
- "HTTP/1.1 401"
- "Authorization:"
- "WWW-Authenticate:"
6.3 使用web_get_int_property检查状态码
web_url("api_call", "URL=http://example.com/api", LAST); int http_status = web_get_int_property(HTTP_INFO_RETURN_CODE); if (http_status == 401) { lr_error_message("认证失败,HTTP状态码: %d", http_status); // 处理认证失败逻辑 }7. 最佳实践与经验分享
- 认证信息参数化:永远不要在脚本中硬编码用户名密码
lr_save_string(lr_eval_string("{vuser}_user"), "username");- 错误处理:为认证失败添加专门的错误处理逻辑
- 环境隔离:确保测试环境与录制环境一致
- 定期维护:随着系统认证机制升级更新测试脚本
一个健壮的认证处理框架示例:
vuser_init() { // 初始化认证信息 if (do_authentication() != LR_PASS) { lr_exit(LR_EXIT_VUSER_FAIL, "初始化认证失败"); } return 0; } action() { // 主业务逻辑 if (call_protected_api() == 401) { // 尝试刷新Token if (refresh_token() != LR_PASS) { // 刷新失败,重新认证 if (do_authentication() != LR_PASS) { lr_exit(LR_EXIT_VUSER_FAIL, "重新认证失败"); } } // 重试原始请求 call_protected_api(); } return 0; } int do_authentication() { // 实现认证逻辑 // 返回LR_PASS或LR_FAIL } int refresh_token() { // 实现Token刷新逻辑 // 返回LR_PASS或LR_FAIL }在实际项目中,认证问题往往是性能测试脚本中最常见的故障点之一。掌握这些认证处理技巧可以显著提高脚本的稳定性和可靠性。