: Web Service security UserNameToken 概念

原理:用户在发送请求的时候,在Soap head中加入自己的用户名以及密码,接受请求的Service通过之前与Client建立的共享密码来验证密码的合法性从而实现鉴别用户的功能。

 
  
  1. <wsse:UsernameToken>  
  2.     <wsse:Username>NNK</wsse:Username>  
  3.     <wsse:Password Type="...#PasswordDigest">  
  4.          weYI3nXd8LjMNVksCKFV8t3rgHh3Rw==  
  5.     </wsse:Password>  
  6.     <wsse:Nonce>WScqanjCEAC4mQoBE07sAQ==</wsse:Nonce>  
  7.     <wsu:Created>2003-07-16T01:24:32Z</wsu:Created>  
  8. </wsse:UsernameToken> 

Password_Digest = Base64 ( SHA-1 ( nonce + created + password ) )
wsse:Nonce
wsu:Created这两个元素的作用:是为了避免重放(Replay)***。

只要对密码做一些处理就可以从中派生出密钥。当然为了安全起见我们希望每次派生出来的密钥都不一样,这样就可以避免多次使用同一密钥而导致密钥被破解。下面就是WS-Security对密钥派生的元素定义:

 
  
  1. <wsse:UsernameToken wsse:Id=”…”>  
  2.     <wsse:Username></wsse:Username>  
  3.     <wsse11:Salt></wsse11:Salt>  
  4.     <wsse11:Iteration></wsse11:Iteration>  
  5. </wsse:UsernameToken> 

其中Salt是导致密钥变化的因子,Iteration是密钥派生时Hash的次数。
密码的派生公式如下:
K1 = SHA1( password + Salt)  K2 = SHA1( K1 )  … Kn = SHA1 ( Kn-1)

:代码示例

xml文件:

 
  
  1. Request xml: 
  2. <soapenv:Envelope xmlns:soapenv="http://schemas.xmlsoap.org/soap/envelope/" xmlns:web="http://www.webserviceX.NET/"> 
  3.    <soapenv:Header/> 
  4.    <soapenv:Body> 
  5.       <web:ConversionRate> 
  6.          <web:FromCurrency>1</web:FromCurrency> 
  7.          <web:ToCurrency>2</web:ToCurrency> 
  8.       </web:ConversionRate> 
  9.    </soapenv:Body> 
  10. </soapenv:Envelope> 
  11.  
  12. Response xml: 
  13. <soapenv:Envelope xmlns:soapenv="http://schemas.xmlsoap.org/soap/envelope/" xmlns:web="http://www.webserviceX.NET/"> 
  14.    <soapenv:Header/> 
  15.    <soapenv:Body> 
  16.       <web:ConversionRateResponse> 
  17.          <web:ConversionRateResult>88</web:ConversionRateResult> 
  18.       </web:ConversionRateResponse> 
  19.    </soapenv:Body> 
  20. </soapenv:Envelope> 

 

1 直接使用httpclient调用service

 

 
  
  1.   public static String soapSpecialConnection(String url) throws Exception 
  2.     { 
  3.     //拼装soap请求报文 
  4.         StringBuilder sb = new StringBuilder(); 
  5.         StringBuilder soapHeader = new StringBuilder(); 
  6.         soapHeader.append("<SOAP-ENV:Envelope xmlns:SOAP-ENV=\"http://schemas.xmlsoap.org/soap/envelope/\" xmlns:web=\"http://www.webserviceX.NET/\">"); 
  7.         soapHeader.append("<SOAP-ENV:Header/>"); 
  8.         soapHeader.append("<SOAP-ENV:Body>"); 
  9.         soapHeader.append("<web:ConversionRate>"); 
  10.         soapHeader.append("<web:FromCurrency>123</web:FromCurrency>"); 
  11.         soapHeader.append("<web:ToCurrency>123</web:ToCurrency>"); 
  12.         soapHeader.append("</web:ConversionRate>"); 
  13.         soapHeader.append("</SOAP-ENV:Body>"); 
  14.         soapHeader.append("</SOAP-ENV:Envelope>"); 
  15.          
  16.         //设置soap请求报文的相关属性 
  17.         URL u = new URL(url); 
  18.         HttpURLConnection conn = (HttpURLConnection) u.openConnection(); 
  19.         conn.setDoInput(true); 
  20.         conn.setDoOutput(true); 
  21.         conn.setUseCaches(false); 
  22.         conn.setDefaultUseCaches(false); 
  23.         conn.setRequestProperty("Host""localhost:8080"); 
  24.         conn.setRequestProperty("Content-Type""text/xml; charset=utf-8"); 
  25.         conn.setRequestProperty("Content-Length", String.valueOf(soapHeader.length())); 
  26.         conn.setRequestProperty("SOAPAction"""); 
  27.         conn.setRequestMethod("POST"); 
  28.         //定义输出流 
  29.         OutputStream output = conn.getOutputStream(); 
  30.         if (null != soapHeader) { 
  31.             byte[] b = soapHeader.toString().getBytes("utf-8"); 
  32.             //发送soap请求报文 
  33.             output.write(b, 0, b.length); 
  34.         } 
  35.         output.flush(); 
  36.         output.close(); 
  37.         //定义输入流,获取soap响应报文 
  38.         InputStream input = conn.getInputStream(); 
  39.         int c = -1
  40.         //sb为返回的soap响应报文字符串 
  41.         while (-1 != (c = input.read())) { 
  42.             sb.append((char)c); 
  43.         } 
  44.         input.close(); 
  45.         return sb.toString();      

 

2 使用apacheaxis 来调用service

 
  
  1. private void callRequest() throws SOAPException { 
  2.         String    NAMESPACE_URI = "http://www.webserviceX.NET/"
  3.         String    PREFIX        = "web"
  4.         String url = "http://localhost:28080/MockService"
  5.          
  6.          SOAPConnectionFactory connectionFactory=SOAPConnectionFactory.newInstance(); 
  7.          MessageFactory        messageFactory=MessageFactory.newInstance(); 
  8.          SOAPFactory           soapFactory = SOAPFactory.newInstance(); 
  9.          
  10.         SOAPMessage message = messageFactory.createMessage(); 
  11.         SOAPEnvelope envelope = message.getSOAPPart().getEnvelope(); 
  12.         envelope.addNamespaceDeclaration(PREFIX, NAMESPACE_URI); 
  13.         Name requestName = soapFactory.createName("ConversionRate", PREFIX, NAMESPACE_URI); 
  14.         SOAPBodyElement trackRequestElement = message.getSOAPBody().addBodyElement(requestName); 
  15.         SOAPElement element1, element2; 
  16.  
  17.         element1 = trackRequestElement.addChildElement(soapFactory.createName("FromCurrency", PREFIX, NAMESPACE_URI)); 
  18.         element2 = trackRequestElement.addChildElement(soapFactory.createName("ToCurrency", PREFIX, NAMESPACE_URI)); 
  19.         element1.addTextNode("123"); 
  20.         element2.addTextNode("123"); 
  21.  
  22.         MimeHeaders hd = message.getMimeHeaders(); 
  23.         hd.setHeader("SOAPAction"""); 
  24.         hd.setHeader("Content-Type""text/xml; charset=utf-8"); 
  25.  
  26.         SOAPConnection connection = connectionFactory.createConnection(); 
  27.         SOAPMessage response = connection.call(message, url);      
  28.     } 

 

3 输出为xml,便于调试

 
  
  1. public void wirteToxml(String fileName, SOAPMessage request) throws Exception { 
  2.       FileWriter fw = new FileWriter(fileName, true); // outputFile为要写入的.xml文件,如result.xml 
  3.       BufferedWriter bw = new BufferedWriter(fw); 
  4.       Source source = request.getSOAPPart().getContent(); 
  5.       Transformer transformer = TransformerFactory.newInstance().newTransformer(); 
  6.       ByteArrayOutputStream myOutStr = new ByteArrayOutputStream(); 
  7.       StreamResult res = new StreamResult(); 
  8.       res.setOutputStream(myOutStr); 
  9.       transformer.transform(source, res); 
  10.       String temp = myOutStr.toString().trim(); 
  11.  
  12.       bw.write(temp); 
  13.       bw.newLine(); 
  14.       bw.flush(); 
  15.       bw.close(); 
  16.   } 

 

4 设置 web service security

 

 
  
  1.   protected void buildHeader(SOAPMessage message) throws SOAPException { 
  2.         String username = "1234"
  3.         String password = "1234"
  4.         final String SECURITY_PREFIX = "wsse"
  5.         SOAPEnvelope envelope = message.getSOAPPart().getEnvelope(); 
  6.         SOAPHeader soapHead = message.getSOAPHeader(); 
  7.         SOAPHeaderElement security = soapHead.addHeaderElement(envelope.createName("Security", SECURITY_PREFIX, 
  8.                                                                                    "http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-secext-1.0.xsd")); 
  9.         security.setMustUnderstand(true); // 服务方必须能够识别校验,否则失败 
  10.  
  11.         SOAPElement usernameToken = security.addChildElement("UsernameToken", SECURITY_PREFIX); 
  12.         usernameToken.addNamespaceDeclaration("wsu"
  13.                                               "http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-utility-1.0.xsd"); 
  14.  
  15.         SOAPElement usernameNode = usernameToken.addChildElement("Username", SECURITY_PREFIX); 
  16.         usernameNode.setValue(username); 
  17.  
  18.         SOAPElement passwordNode = usernameToken.addChildElement("Password", SECURITY_PREFIX); 
  19.         passwordNode.setAttribute("Type"
  20.                                   "http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-username-token-profile-1.0#PasswordText"); 
  21.         passwordNode.setValue(password); 
  22. }  

mustUnderstand:用于标注security header是否必须被service端解析处理

三:测试工具

TCPMon :   http://ws.apache.org/commons/tcpmon/tcpmontutorial.html 可视化发送请求的信息,以及返回结果的信息,便于调试