前言
有時候調用web service 會出現
Message does not conform to configured policy [ AuthenticationTokenPolicy(S) ]: No Security Header found
這樣的錯誤。
以在 soapui 調用的結果來看, 會出現如下的返回
出現這種錯誤的原因 是webservice 的服務端需要提供 soap 認證的表頭。
舉例來說, 可能需要加上如下的認證頭:
<soapenv:Header> <wsse:Security xmlns:wsse="http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-secext-1.0.xsd" soapenv:mustUnderstand="1"> <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#PasswordText">Password</wsse:Password> </wsse:UsernameToken> </wsse:Security> </soapenv:Header>
(這個細部的格式和服務端的要求有關, 具體的username和pass也是服務端提供的)
在sopaui 調用的時候, 加上類似, 就能呼叫成功了。
在soapui 調用, 可以用以上方式來做。在把wsdl 轉為java 後, 又該如何加上認證的頭資訊呢。
cxf 加上認證頭
(以上的認證頭, 比較接近cxf 的調用方式。)
如果使用的是cxf生產的用戶端的代碼。
(如何產生,參考 CXF 產生Web Service Client(將WSDl 轉化成 Java代碼))
在 _Client 調用的時候加上 如下代碼:(在方法調用的代碼之前)
Map<String, Object> props = new HashMap<String, Object>(); props.put(WSHandlerConstants.ACTION, WSHandlerConstants.USERNAME_TOKEN); props.put(WSHandlerConstants.PASSWORD_TYPE,WSConstants.PW_TEXT); props.put(WSHandlerConstants.USER, "UserName"); props.put(WSHandlerConstants.PW_CALLBACK_CLASS, PasswordHandler.class.getName()); WSS4JOutInterceptor wssOut = new WSS4JOutInterceptor(props); Client client = ClientProxy.getClient(port); client.getOutInterceptors().add(wssOut);
在client 的java 檔案中, 新增以下內部類
public static class PasswordHandler implements CallbackHandler{ public void handle(javax.security.auth.callback.Callback[] callbacks) { for (int i = 0; i < callbacks.length; i++) { WSPasswordCallback pc = (WSPasswordCallback)callbacks[i]; pc.setPassword("password"); } }}
Axis2 加上認證頭
針對以上的認證頭在axis2 產生的java 檔案中如何添加呢(Axis2自動產生的java 檔案並不會自動產生main的測試檔案, 需要自己寫。 XXXXProxy.java 這是供調用的類檔案。 不過這些和添加認證頭關係不大)
axis2會產生一個 XXXXPortBindingStub.java 的檔案。 這裡面的內容就是實際的方法體。
找到我們需要調用的那個方法體:
在方法調用之前,加入以下代碼:
//Begin add for Header String AUTH_PREFIX = "wsse"; String AUTH_NS = "http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-secext-1.0.xsd"; try{ SOAPElement wsSecHeaderElm = soapFactory.createElement("Security", AUTH_PREFIX, AUTH_NS); SOAPElement userNameTokenElm = soapFactory.createElement("UsernameToken",AUTH_PREFIX, AUTH_NS); SOAPElement userNameElm = soapFactory.createElement("Username",AUTH_PREFIX, AUTH_NS); SOAPElement passwdElm = soapFactory.createElement("Password",AUTH_PREFIX, AUTH_NS); passwdElm.setAttribute("Type", "http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-username-token-profile-1.0#PasswordText"); userNameElm.addTextNode("vend_bmc01"); passwdElm.addTextNode("mediatek"); userNameTokenElm.addChildElement(userNameElm); userNameTokenElm.addChildElement(passwdElm); wsSecHeaderElm.addChildElement(userNameTokenElm); SOAPHeaderElement soapHeaderElement = new SOAPHeaderElement(wsSecHeaderElm); soapHeaderElement.setMustUnderstand(true); _call.addHeader(soapHeaderElement); }catch(Exception e) { e.printStackTrace(); } //End add for Header ===》call method java.lang.Object _resp = _call.invoke(XXXX);
原理很簡單。 加上類似xml 的頭, _call_addHeader
加上之後,再觸發方法 _call.invoke