如何在JMeter中修改Password_Digest并为SOAP请求添加WSS与WS-Addressing?
我帮你梳理下SOAP请求添加WSS UsernameToken(带Password Digest)和WS-Addressing的正确姿势,还有JMeter里的具体操作步骤——
1. 先搞对WSS UsernameToken的Password_Digest生成逻辑
你给出的公式是对的,但要注意几个容易踩坑的细节:Password_Digest = Base64 ( SHA-1 ( nonce + created + Base64 ( SHA-1 ( password ) ) ) )
- nonce:必须是随机唯一的二进制值(比如16字节随机串),每次请求都要换,而且要同时在UsernameToken里传递它的Base64编码值
- created:必须是UTC时区的ISO 8601格式时间戳,比如
2024-05-20T14:23:00Z,要和nonce一一对应 - 计算步骤拆解(别搞混字节和字符串):
- 先对原始密码做SHA-1哈希,再转Base64,得到
SHA1_PWD_BASE64 - 生成16字节随机nonce,转Base64得到
NONCE_BASE64(用于XML里的Nonce字段),同时保留原始二进制nonce用于后续拼接 - 获取当前UTC时间的ISO 8601字符串
CREATED - 把原始二进制nonce +
CREATED的UTF-8字节 +SHA1_PWD_BASE64的UTF-8字节拼接在一起 - 对拼接后的字节做SHA-1哈希,再转Base64就是最终的Password_Digest
- 先对原始密码做SHA-1哈希,再转Base64,得到
示例WSS UsernameToken XML结构
<wsse:Security xmlns:wsse="http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-secext-1.0.xsd"> <wsse:UsernameToken> <wsse:Username>your_username</wsse:Username> <wsse:Password Type="http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-username-token-profile-1.0#PasswordDigest">GENERATED_DIGEST</wsse:Password> <wsse:Nonce EncodingType="http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-soap-message-security-1.0#Base64Binary">NONCE_BASE64</wsse:Nonce> <wsu:Created xmlns:wsu="http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-utility-1.0.xsd">2024-05-20T14:23:00Z</wsu:Created> </wsse:UsernameToken> </wsse:Security>
2. 给SOAP请求添加WS-Addressing(WSA)
WS-Addressing需要在SOAP Header里添加几个核心元素,注意命名空间要和服务端要求一致(通常是http://www.w3.org/2005/08/addressing,少数用旧版2004的):
<soapenv:Header> <wsa:To xmlns:wsa="http://www.w3.org/2005/08/addressing">https://your-soap-endpoint-url</wsa:To> <wsa:Action xmlns:wsa="http://www.w3.org/2005/08/addressing">http://your-target-namespace/your-operation-name</wsa:Action> <wsa:MessageID xmlns:wsa="http://www.w3.org/2005/08/addressing">urn:uuid:3e4a7f9b-1c2d-5e6f-7g8h-9i0j1k2l3m4n</wsa:MessageID> <wsa:ReplyTo xmlns:wsa="http://www.w3.org/2005/08/addressing"> <wsa:Address>http://www.w3.org/2005/08/addressing/anonymous</wsa:Address> </wsa:ReplyTo> </soapenv:Header>
wsa:Action必须严格匹配你调用的SOAP操作的命名空间+操作名,比如服务端定义的http://example.com/orders/CreateOrderwsa:MessageID每次请求要生成新的UUID,避免重复
3. JMeter里的具体配置步骤
3.1 用JSR223 PreProcessor计算动态参数
JMeter自带函数搞不定这么复杂的哈希逻辑,直接用Groovy脚本(性能最优)生成所有动态变量:
- 在你的SOAP请求Sampler下添加JSR223 PreProcessor
- 语言选择
Groovy,粘贴以下脚本(替换username和password为你的实际值):
import java.security.MessageDigest import java.util.Base64 import java.util.UUID import java.text.SimpleDateFormat import java.util.TimeZone // 配置你的账号密码 def username = "your_real_username" def password = "your_real_password" // 步骤1:计算密码的SHA1->Base64 MessageDigest sha1 = MessageDigest.getInstance("SHA-1") byte[] pwdSha1Bytes = sha1.digest(password.getBytes("UTF-8")) def pwdSha1Base64 = Base64.getEncoder().encodeToString(pwdSha1Bytes) // 步骤2:生成随机nonce byte[] nonceBytes = new byte[16] new Random().nextBytes(nonceBytes) def nonceBase64 = Base64.getEncoder().encodeToString(nonceBytes) // 步骤3:生成UTC时间戳 SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ss'Z'") sdf.setTimeZone(TimeZone.getTimeZone("UTC")) def created = sdf.format(new Date()) // 步骤4:拼接字节并计算最终Digest byte[] createdBytes = created.getBytes("UTF-8") byte[] pwdSha1Base64Bytes = pwdSha1Base64.getBytes("UTF-8") byte[] combinedBytes = new byte[nonceBytes.length + createdBytes.length + pwdSha1Base64Bytes.length] System.arraycopy(nonceBytes, 0, combinedBytes, 0, nonceBytes.length) System.arraycopy(createdBytes, 0, combinedBytes, nonceBytes.length, createdBytes.length) System.arraycopy(pwdSha1Base64Bytes, 0, combinedBytes, nonceBytes.length + createdBytes.length, pwdSha1Base64Bytes.length) byte[] digestBytes = sha1.digest(combinedBytes) def passwordDigest = Base64.getEncoder().encodeToString(digestBytes) // 把变量存入JMeter上下文,供SOAP请求使用 vars.put("username", username) vars.put("passwordDigest", passwordDigest) vars.put("nonceBase64", nonceBase64) vars.put("created", created) vars.put("messageId", "urn:uuid:" + UUID.randomUUID())
3.2 修改SOAP请求的XML模板
把SOAP请求的Body里的静态值替换成JMeter变量,示例如下:
<soapenv:Envelope xmlns:soapenv="http://schemas.xmlsoap.org/soap/envelope/" xmlns:ord="http://example.com/orders"> <soapenv:Header> <!-- WS-Addressing部分 --> <wsa:To xmlns:wsa="http://www.w3.org/2005/08/addressing">https://your-soap-endpoint.com/orders</wsa:To> <wsa:Action xmlns:wsa="http://www.w3.org/2005/08/addressing">http://example.com/orders/CreateOrder</wsa:Action> <wsa:MessageID xmlns:wsa="http://www.w3.org/2005/08/addressing">${messageId}</wsa:MessageID> <wsa:ReplyTo xmlns:wsa="http://www.w3.org/2005/08/addressing"> <wsa:Address>http://www.w3.org/2005/08/addressing/anonymous</wsa:Address> </wsa:ReplyTo> <!-- WSS UsernameToken部分 --> <wsse:Security xmlns:wsse="http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-secext-1.0.xsd"> <wsse:UsernameToken> <wsse:Username>${username}</wsse:Username> <wsse:Password Type="http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-username-token-profile-1.0#PasswordDigest">${passwordDigest}</wsse:Password> <wsse:Nonce EncodingType="http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-soap-message-security-1.0#Base64Binary">${nonceBase64}</wsse:Nonce> <wsu:Created xmlns:wsu="http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-utility-1.0.xsd">${created}</wsu:Created> </wsse:UsernameToken> </wsse:Security> </soapenv:Header> <soapenv:Body> <!-- 你的SOAP请求体 --> <ord:CreateOrder> <ord:OrderID>12345</ord:OrderID> <ord:Amount>99.99</ord:Amount> </ord:CreateOrder> </soapenv:Body> </soapenv:Envelope>
3.3 调试和验证
- 添加View Results Tree sampler,查看实际发送的XML是否正确替换了所有变量
- 如果还是失败,重点检查:
- 是否用了原始二进制nonce参与拼接(别误用Base64后的字符串)
- created时间是否是UTC时区,格式是否正确
- WS-Addressing的命名空间是否和服务端要求一致
- 有些服务端可能对Password_Digest的拼接顺序有调整,比如把created放在nonce前面,可以尝试修改脚本里的拼接顺序
内容的提问来源于stack exchange,提问作者Pixi Dixi
相关产品推荐
相关产品推荐

