一: Web Service security UserNameToken 概念
原理:用户在发送请求的时候,在Soap head中加入自己的用户名以及密码,接受请求的Service通过之前与Client建立的共享密码来验证密码的合法性从而实现鉴别用户的功能。
- <wsse:UsernameToken>
- <wsse:Username>NNK</wsse:Username>
- <wsse:Password Type="...#PasswordDigest">
- weYI3nXd8LjMNVksCKFV8t3rgHh3Rw==
- </wsse:Password>
- <wsse:Nonce>WScqanjCEAC4mQoBE07sAQ==</wsse:Nonce>
- <wsu:Created>2003-07-16T01:24:32Z</wsu:Created>
- </wsse:UsernameToken>
Password_Digest = Base64 ( SHA-1 ( nonce + created + password ) )
wsse:Nonce和wsu:Created这两个元素的作用:是为了避免重放(Replay)攻击。
只要对密码做一些处理就可以从中派生出密钥。当然为了安全起见我们希望每次派生出来的密钥都不一样,这样就可以避免多次使用同一密钥而导致密钥被破解。下面就是WS-Security对密钥派生的元素定义:
- <wsse:UsernameToken wsse:Id=”…”>
- <wsse:Username>…</wsse:Username>
- <wsse11:Salt>…</wsse11:Salt>
- <wsse11:Iteration>…</wsse11:Iteration>
- </wsse:UsernameToken>
其中Salt是导致密钥变化的因子,Iteration是密钥派生时Hash的次数。
密码的派生公式如下:
K1 = SHA1( password + Salt) K2 = SHA1( K1 ) … Kn = SHA1 ( Kn-1)
二:代码示例
xml文件:
- Request xml:
- <soapenv:Envelope xmlns:soapenv="http://schemas.xmlsoap.org/soap/envelope/" xmlns:web="http://www.webserviceX.NET/">
- <soapenv:Header/>
- <soapenv:Body>
- <web:ConversionRate>
- <web:FromCurrency>1</web:FromCurrency>
- <web:ToCurrency>2</web:ToCurrency>
- </web:ConversionRate>
- </soapenv:Body>
- </soapenv:Envelope>
- Response xml:
- <soapenv:Envelope xmlns:soapenv="http://schemas.xmlsoap.org/soap/envelope/" xmlns:web="http://www.webserviceX.NET/">
- <soapenv:Header/>
- <soapenv:Body>
- <web:ConversionRateResponse>
- <web:ConversionRateResult>88</web:ConversionRateResult>
- </web:ConversionRateResponse>
- </soapenv:Body>
- </soapenv:Envelope>
1 直接使用httpclient调用service
- public static String soapSpecialConnection(String url) throws Exception
- {
- //拼装soap请求报文
- StringBuilder sb = new StringBuilder();
- StringBuilder soapHeader = new StringBuilder();
- soapHeader.append("<SOAP-ENV:Envelope xmlns:SOAP-ENV=\"http://schemas.xmlsoap.org/soap/envelope/\" xmlns:web=\"http://www.webserviceX.NET/\">");
- soapHeader.append("<SOAP-ENV:Header/>");
- soapHeader.append("<SOAP-ENV:Body>");
- soapHeader.append("<web:ConversionRate>");
- soapHeader.append("<web:FromCurrency>123</web:FromCurrency>");
- soapHeader.append("<web:ToCurrency>123</web:ToCurrency>");
- soapHeader.append("</web:ConversionRate>");
- soapHeader.append("</SOAP-ENV:Body>");
- soapHeader.append("</SOAP-ENV:Envelope>");
- //设置soap请求报文的相关属性
- URL u = new URL(url);
- HttpURLConnection conn = (HttpURLConnection) u.openConnection();
- conn.setDoInput(true);
- conn.setDoOutput(true);
- conn.setUseCaches(false);
- conn.setDefaultUseCaches(false);
- conn.setRequestProperty("Host", "localhost:8080");
- conn.setRequestProperty("Content-Type", "text/xml; charset=utf-8");
- conn.setRequestProperty("Content-Length", String.valueOf(soapHeader.length()));
- conn.setRequestProperty("SOAPAction", "");
- conn.setRequestMethod("POST");
- //定义输出流
- OutputStream output = conn.getOutputStream();
- if (null != soapHeader) {
- byte[] b = soapHeader.toString().getBytes("utf-8");
- //发送soap请求报文
- output.write(b, 0, b.length);
- }
- output.flush();
- output.close();
- //定义输入流,获取soap响应报文
- InputStream input = conn.getInputStream();
- int c = -1;
- //sb为返回的soap响应报文字符串
- while (-1 != (c = input.read())) {
- sb.append((char)c);
- }
- input.close();
- return sb.toString();
- }
2 使用apache的axis 来调用service
- private void callRequest() throws SOAPException {
- String NAMESPACE_URI = "http://www.webserviceX.NET/";
- String PREFIX = "web";
- String url = "http://localhost:28080/MockService";
- SOAPConnectionFactory connectionFactory=SOAPConnectionFactory.newInstance();
- MessageFactory messageFactory=MessageFactory.newInstance();
- SOAPFactory soapFactory = SOAPFactory.newInstance();
- SOAPMessage message = messageFactory.createMessage();
- SOAPEnvelope envelope = message.getSOAPPart().getEnvelope();
- envelope.addNamespaceDeclaration(PREFIX, NAMESPACE_URI);
- Name requestName = soapFactory.createName("ConversionRate", PREFIX, NAMESPACE_URI);
- SOAPBodyElement trackRequestElement = message.getSOAPBody().addBodyElement(requestName);
- SOAPElement element1, element2;
- element1 = trackRequestElement.addChildElement(soapFactory.createName("FromCurrency", PREFIX, NAMESPACE_URI));
- element2 = trackRequestElement.addChildElement(soapFactory.createName("ToCurrency", PREFIX, NAMESPACE_URI));
- element1.addTextNode("123");
- element2.addTextNode("123");
- MimeHeaders hd = message.getMimeHeaders();
- hd.setHeader("SOAPAction", "");
- hd.setHeader("Content-Type", "text/xml; charset=utf-8");
- SOAPConnection connection = connectionFactory.createConnection();
- SOAPMessage response = connection.call(message, url);
- }
3 输出为xml,便于调试
- public void wirteToxml(String fileName, SOAPMessage request) throws Exception {
- FileWriter fw = new FileWriter(fileName, true); // outputFile为要写入的.xml文件,如result.xml
- BufferedWriter bw = new BufferedWriter(fw);
- Source source = request.getSOAPPart().getContent();
- Transformer transformer = TransformerFactory.newInstance().newTransformer();
- ByteArrayOutputStream myOutStr = new ByteArrayOutputStream();
- StreamResult res = new StreamResult();
- res.setOutputStream(myOutStr);
- transformer.transform(source, res);
- String temp = myOutStr.toString().trim();
- bw.write(temp);
- bw.newLine();
- bw.flush();
- bw.close();
- }
4 设置 web service security
- protected void buildHeader(SOAPMessage message) throws SOAPException {
- String username = "1234";
- String password = "1234";
- final String SECURITY_PREFIX = "wsse";
- SOAPEnvelope envelope = message.getSOAPPart().getEnvelope();
- SOAPHeader soapHead = message.getSOAPHeader();
- SOAPHeaderElement security = soapHead.addHeaderElement(envelope.createName("Security", SECURITY_PREFIX,
- "http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-secext-1.0.xsd"));
- security.setMustUnderstand(true); // 服务方必须能够识别校验,否则失败
- SOAPElement usernameToken = security.addChildElement("UsernameToken", SECURITY_PREFIX);
- usernameToken.addNamespaceDeclaration("wsu",
- "http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-utility-1.0.xsd");
- SOAPElement usernameNode = usernameToken.addChildElement("Username", SECURITY_PREFIX);
- usernameNode.setValue(username);
- SOAPElement passwordNode = usernameToken.addChildElement("Password", SECURITY_PREFIX);
- passwordNode.setAttribute("Type",
- "http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-username-token-profile-1.0#PasswordText");
- passwordNode.setValue(password);
- }
mustUnderstand:用于标注security header是否必须被service端解析处理
三:测试工具
TCPMon : http://ws.apache.org/commons/tcpmon/tcpmontutorial.html 可视化发送请求的信息,以及返回结果的信息,便于调试