为了使用RPC web服务,首先我必须调用web方法'Authentication',然后我应该能够调用其他web方法。我已经尝试实现一个spring web客户端来使用这个服务,但是有些事情是非常错误的。
这个服务最初是为php用户设计的,他们提供的示例客户端代码如下:
$soap = new SoapClient('http://www.2972.ir/wsdl?XML');
if ($soap->Authentication('user', '123456')) {
echo $soap->getCredit();
}
'authentication'方法成功时将返回1,否则返回-1。'getCredit'方法返回显示用户当前余额的浮点数。
现在我在Java中的实现:
@Service
public class MessageService implements IMessageService {
@Autowired
private IranSMSPanelPortType webService;
public String doSomething() {
int status = webService.authentication("user", "123456");
System.out.println("# status: "+status);
return String.valueOf(webService.getCredit());
}
我得到的状态码等于'1',但在调用第二个web方法时,我得到这个异常:
javax.xml.ws.soap.SOAPFaultException: You are not authorized
很明显,在php中调用'authentication'方法与在java中调用它没有相同的效果。于是我又调查了一下,发现在'authentication' web方法的响应cookie中设置了一些东西。
下面是响应的概览:
<<p> 头/strong>HTTP/1.1 200 OK
Date: Thu, 07 Aug 2014 17:26:49 GMT
Server: Apache/2
X-Powered-By: PHP/5.4.24
Expires: Thu, 19 Nov 1981 08:52:00 GMT
Cache-Control: no-store, no-cache, must-revalidate, post-check=0, pre-check=0
Pragma: no-cache
Connection: close
Set-Cookie: PHPSESSID=m607671c9d8aeks5vm2r84n8r3; path=/
Vary: Accept-Encoding,User-Agent
Transfer-Encoding: chunked
Content-Type: text/xml; charset=utf-8
<<p> 信封/strong> <?xml version="1.0" encoding="UTF-8"?>
<SOAP-ENV:Envelope xmlns:SOAP-ENV="http://schemas.xmlsoap.org/soap/envelope/" xmlns:ns1="http://www.iransmspanel.com/" xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:SOAP-ENC="http://schemas.xmlsoap.org/soap/encoding/" SOAP-ENV:encodingStyle="http://schemas.xmlsoap.org/soap/encoding/">
<SOAP-ENV:Body>
<ns1:AuthenticationResponse>
<result xsi:type="xsd:int">1</result>
</ns1:AuthenticationResponse>
</SOAP-ENV:Body>
</SOAP-ENV:Envelope>
可以看到PHPSESSID已经被赋值了!这个参数很可能是用来处理授权的。
给定以下Spring Web Service配置,我如何实现相同的结果?
<bean id="smsWebService" class="org.springframework.remoting.jaxws.JaxWsPortProxyFactoryBean">
<property name="serviceInterface" value="com.iransmspanel.IranSMSPanelPortType" />
<property name="wsdlDocumentUrl" value="http://www.2972.ir/wsdl?XML" />
<property name="namespaceUri" value="http://www.iransmspanel.com/" />
<property name="serviceName" value="IranSMSPanel" />
<property name="portName" value="IranSMSPanelPort" />
</bean>
如果需要处理身份验证,我欢迎web服务客户端的其他实现。
我最终设法解决了这个问题,使用自定义的SAOP-Handler并手动从消息Http Headers中提取PHPSESSID。
Spring配置
<bean id="smsWebService" class="org.springframework.remoting.jaxws.JaxWsPortProxyFactoryBean">
...
<property name="handlerResolver" ref="credentialHandlerResolver"/>
</bean>
<bean id="credentialHandlerResolver" class="com.example.handlers.CredentialHandlerResolver"/>
CredentialHandlerResolver.java
import javax.xml.ws.handler.Handler;
import javax.xml.ws.handler.HandlerResolver;
import javax.xml.ws.handler.PortInfo;
...
public class CredentialHandlerResolver implements HandlerResolver {
private List<Handler> chain;
CredentialHandlerResolver() {
chain = new ArrayList<Handler>();
chain.add(new CredentialHandler());
}
public List<Handler> getHandlerChain(PortInfo portInfo) {
return chain;
}
}
CredentialHandler.java
public class CredentialHandler implements SOAPHandler<SOAPMessageContext> {
public static String PHPSESSID;
public boolean handleMessage(SOAPMessageContext context) {
Boolean isRequest = (Boolean) context.get(MessageContext.MESSAGE_OUTBOUND_PROPERTY);
try {
SOAPMessage message = context.getMessage();
SOAPBody body = message.getSOAPPart().getEnvelope().getBody();
if(!isRequest && body.getFirstChild().getLocalName().equals("AuthenticationResponse") && body.getFirstChild().getTextContent().equals("1")) {
// extracting PHPSESSID from HTTP headers
Map<String, List<String>> httpHeaders = (Map<String, List<String>>) context.get(MessageContext.HTTP_RESPONSE_HEADERS);
String session = httpHeaders.get("Set-Cookie").get(0);
PHPSESSID = session.substring(10, session.indexOf(';'));
}
else if (isRequest && PHPSESSID != null && !body.getFirstChild().getLocalName().equals("Authentication")){
// add PHPSESSID to HTTP headers
Map<String, List<String>> headers = new HashMap<String, List<String>>();
headers.put("Cookie", Arrays.asList("PHPSESSID=" + PHPSESSID));
context.put(MessageContext.HTTP_REQUEST_HEADERS, headers);
}
}
catch (SOAPException ex) {
ex.printStackTrace();
return false;
}
return true;
}