使用JMeter进行API性能测试时排查403认证错误
问题:API性能测试持续返回403认证错误(预期200)
我正在对应用程序API开展性能测试,发送请求时始终收到403(认证错误)响应,但预期应为200(请求成功)。
已完成的排查步骤:
- 向API发送包含有效参数的请求;
- 请求中携带了我认为有效的Bearer Token;
- 验证URL构造正确;
- 测试无需认证的健康检查端点,可正常返回结果。
尽管采取上述措施,仍持续收到403错误,说明Bearer Token的配置可能存在问题——即便我认为Token有效,API仍无法识别或接受它。
以下是我使用的JMeter Java代码:
package initialization; import org.apache.jmeter.control.LoopController; import org.apache.jmeter.control.gui.LoopControlPanel; import org.apache.jmeter.control.gui.TestPlanGui; import org.apache.jmeter.engine.JMeterEngineException; import org.apache.jmeter.engine.StandardJMeterEngine; import org.apache.jmeter.protocol.http.control.HeaderManager; import org.apache.jmeter.protocol.http.control.gui.HttpTestSampleGui; import org.apache.jmeter.protocol.http.sampler.HTTPSamplerProxy; import org.apache.jmeter.reporters.ResultCollector; import org.apache.jmeter.reporters.Summariser; import org.apache.jmeter.testelement.TestElement; import org.apache.jmeter.testelement.TestPlan; import org.apache.jmeter.threads.ThreadGroup; import org.apache.jmeter.threads.gui.ThreadGroupGui; import org.apache.jmeter.util.JMeterUtils; import org.apache.jorphan.collections.ListedHashTree; import org.testng.annotations.Test; import java.io.IOException; public class JmeterSetup { private static final String API_URL = ""; private static final String ACCEPT_HEADER = "*/*"; private static final String AUTHORIZATION_HEADER = "Bearer ActualBearer"; private static final String Param = "sd"; @Test void givenJMeterScript_whenUsingCode_thenExecuteViaJavaProgram() throws IOException, JMeterEngineException { JMeterUtils.setJMeterHome(".\\Downloads\\apache-jmeter-5.6.3\\bin\\jmeter.properties"); String jmeterFile = ".\\Downloads\\apache-jmeter-5.6.3\\bin\\jmeter.properties"; JMeterUtils.loadJMeterProperties(jmeterFile); JMeterUtils.initLocale(); StandardJMeterEngine jmeter = new StandardJMeterEngine(); HTTPSamplerProxy httpSampler = getHttpSamplerProxy(); LoopController loopController = getLoopController(); ThreadGroup threadGroup = getThreadGroup(loopController); TestPlan testPlan = getTestPlan(threadGroup); ListedHashTree testPlanTree = new ListedHashTree(); ListedHashTree threadGroupHashTree = (ListedHashTree) testPlanTree.add(testPlan, threadGroup); threadGroupHashTree.add(httpSampler); Summariser summer = null; String summariserName = JMeterUtils.getPropDefault("summariser.name", "summary"); if (summariserName.length() > 0) { summer = new Summariser(summariserName); } String logFile = "output-logs.jtl"; ResultCollector logger = new ResultCollector(summer); logger.setFilename(logFile); testPlanTree.add(testPlanTree.getArray()[0], logger); jmeter.configure(testPlanTree); jmeter.run(); System.out.println("Test completed. See output-logs.jtl file for results"); System.out.println("JMeter .jmx script is available at script.jmx"); } private static LoopController getLoopController() { LoopController loopController = new LoopController(); loopController.setLoops(1); loopController.setFirst(true); loopController.setProperty(TestElement.TEST_CLASS, LoopController.class.getName()); loopController.setProperty(TestElement.GUI_CLASS, LoopControlPanel.class.getName()); loopController.initialize(); return loopController; } private static HTTPSamplerProxy getHttpSamplerProxy() { HTTPSamplerProxy httpSampler = new HTTPSamplerProxy(); httpSampler.setProtocol("https"); httpSampler.setDomain("ApplicationDomain"); httpSampler.setPath("Path"); httpSampler.setMethod("GET"); // Create a HeaderManager and add the headers HeaderManager headerManager = new HeaderManager(); headerManager.add(new org.apache.jmeter.protocol.http.control.Header("accept", ACCEPT_HEADER)); headerManager.add(new org.apache.jmeter.protocol.http.control.Header("Authorization", AUTHORIZATION_HEADER)); headerManager.add(new org.apache.jmeter.protocol.http.control.Header("param", Param)); httpSampler.setHeaderManager(headerManager); // Set the properties for the HTTPSamplerProxy httpSampler.setProperty(TestElement.GUI_CLASS, HttpTestSampleGui.class.getName()); httpSampler.setProperty(TestElement.NAME, "API Request"); return httpSampler; } private static TestPlan getTestPlan(ThreadGroup threadGroup) { TestPlan testPlan = new TestPlan("Sample Test Plan"); testPlan.setProperty(TestElement.TEST_CLASS, TestPlan.class.getName()); testPlan.setProperty(TestElement.GUI_CLASS, TestPlanGui.class.getName()); return testPlan; } private static ThreadGroup getThreadGroup(LoopController loopController) { ThreadGroup threadGroup = new ThreadGroup(); threadGroup.setName("Sample Thread Group"); threadGroup.setNumThreads(10); threadGroup.setRampUp(5); threadGroup.setSamplerController(loopController); threadGroup.setProperty(TestElement.TEST_CLASS, ThreadGroup.class.getName()); threadGroup.setProperty(TestElement.GUI_CLASS, ThreadGroupGui.class.getName()); return threadGroup; } }
排查与修复建议
验证Bearer Token的有效性与格式
- 替换
AUTHORIZATION_HEADER中的ActualBearer为真实Token,确保Bearer与Token之间仅有一个空格,无多余字符;部分API对Bearer的大小写敏感,需严格遵循要求。 - 用Postman或curl直接调用API并携带同一Token,确认是否返回200。若仍返回403,说明Token本身无效(过期、权限不足、受众不匹配等),需重新获取有效Token。
- 替换
修正JMeter初始化错误
JMeterUtils.setJMeterHome()方法需传入JMeter根目录路径,而非properties文件路径。正确代码:JMeterUtils.setJMeterHome(".\\Downloads\\apache-jmeter-5.6.3"); String jmeterFile = ".\\Downloads\\apache-jmeter-5.6.3\\bin\\jmeter.properties";- 该错误会导致JMeter无法正确加载配置,可能影响请求发送逻辑。
补全API域名与路径
- 将
httpSampler.setDomain("ApplicationDomain")和httpSampler.setPath("Path")替换为真实的API域名与路径,确保与健康检查端点的环境一致。
- 将
排查请求头与并发限制
- 确认API是否需要额外请求头(如
Content-Type),若有需补充到HeaderManager中。 - 暂时将线程组的
numThreads改为1、rampUp改为0,排除并发请求触发API限流或认证频率限制的可能。
- 确认API是否需要额外请求头(如
抓包或查看日志分析请求细节
- 查看
output-logs.jtl文件中的请求头与响应详情,确认Authorization头是否正确发送;或用抓包工具(如Fiddler)捕获实际请求,对比预期与实际发送的内容差异。
- 查看
内容的提问来源于stack exchange,提问作者Nate101
相关产品推荐
相关产品推荐

