NodeJS/NestJS带WS-Security的SOAP监听器实现及原始请求获取咨询
解决方案:在NestJS中获取原始SOAP请求并实现WS-Security验证
一、获取原始SOAP请求报文
要处理WS-Security验证,核心是拿到完整的原始SOAP请求内容,你可以通过以下两种方式实现:
1. 利用soap库的request事件拦截
soap库的服务端对象提供了request事件,触发时会直接传入原始HTTP请求对象、响应对象和完整的SOAP XML报文,这是最直接的方式:
import { Injectable, OnApplicationBootstrap } from '@nestjs/common'; import { Service } from "./serv/serv.service"; import * as soap from 'soap'; import * as http from 'http'; import { DOMParser } from '@xmldom/xmldom'; @Injectable() export class AppService implements OnApplicationBootstrap { constructor(private readonly serv: Service) {} onApplicationBootstrap() { const server = http.createServer((req, res) => {}); server.listen(3000, () => { console.log('listening on port 3000'); }); const soapServer = soap.listen(server, '/soap', this.serv.process(), this.serv.getWsdl(), (err) => { if (err) { console.error('error binding serv WSDL:', err); } else { console.log('service is running'); } }); // 拦截SOAP请求,获取原始报文并做WS-Security验证 soapServer.on('request', (req, res, rawSoapXml) => { const isValid = this.validateWsSecurity(rawSoapXml); if (!isValid) { // 返回SOAP标准错误响应 res.writeHead(401, {'Content-Type': 'text/xml'}); res.end(`<soapenv:Envelope xmlns:soapenv="http://schemas.xmlsoap.org/soap/envelope/"> <soapenv:Body> <soapenv:Fault> <faultcode>soapenv:Client</faultcode> <faultstring>Invalid WS-Security credentials</faultstring> </soapenv:Fault> </soapenv:Body> </soapenv:Envelope>`); // 返回false终止soap库后续处理 return false; } }); } // 自定义WS-Security验证逻辑 private validateWsSecurity(rawXml: string): boolean { const parser = new DOMParser(); const doc = parser.parseFromString(rawXml, 'text/xml'); const wsseNs = 'http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-secext-1.0.xsd'; // 提取WS-Security头中的用户名令牌 const usernameNode = doc.getElementsByTagNameNS(wsseNs, 'Username')[0]; const passwordNode = doc.getElementsByTagNameNS(wsseNs, 'Password')[0]; if (!usernameNode || !passwordNode) { return false; } // 替换为你的实际用户验证逻辑(比如查询数据库) const validUsername = 'admin'; const validPassword = 'secure123'; return usernameNode.textContent === validUsername && passwordNode.textContent === validPassword; } }
2. 在HTTP服务器层面缓存原始请求体
如果需要更底层的控制,可以在创建HTTP服务器时手动收集原始请求体,挂载到请求对象上供后续使用:
const server = http.createServer((req, res) => { let rawBody = ''; req.on('data', chunk => rawBody += chunk.toString()); req.on('end', () => { // 将原始报文挂载到req对象 (req as any).rawSoapBody = rawBody; }); });
之后可以在soapServer的任意钩子(比如authenticate)中通过req.rawSoapBody获取原始内容。
二、扩展WS-Security验证能力
如果需要处理数字签名、时间戳等复杂WS-Security规则,可以:
- 使用
crypto模块解析证书、验证签名值; - 针对时间戳(
Timestamp元素)检查请求的有效期; - 参考OASIS WS-Security规范实现完整的校验逻辑。
三、替代方案:使用支持服务端WS-Security的库
如果不想从零实现,可以尝试strong-soap(原IBM维护的SOAP库),它对服务端WS-Security的支持更完善,内置了用户名令牌、签名验证等常用功能,无需手动解析原始报文。
内容的提问来源于stack exchange,提问作者Erik
相关产品推荐
相关产品推荐

