How to consume a WCF Web Service that uses custom username validation with a PHP page?
I'm having hard time consuming a secure WCF Web Service from a PHP site. My knowledge in PHP are limited, I found various examples on the Web but didn't succeeded making them working yet.
I have a Silverlight application that also consume this WebService and it works fine. But when I run the PHP site, I get this error :
MessageSecurityException: Security processor was unable to find a security header in the message. This might be because the message is an unsecured fault or because there is a binding mismatch between the communicating parties. This can occur if the service is configured for security and the client is not using security.
I also tried to change the customBinding to a basicHttpBinding. When I do that, the Custom Username validator is not called anymore. But, using basicHttpBinding and removing credentials validation on my WCF Service "works" (Besides the fact that it is unsecured). So the problem seems to related to the security message.
Could someone give me a working example or tutorial that could help me making this PHP page works?
Here is my PHP code :
$options = array(
'soap_version' => SOAP_1_1,
'exceptions' => true,
'trace' => 1,
'cache_wsdl' => WSDL_CACHE_NONE,
'Username' => 'MyUserName',
'password' => 'MyPassword');
$client = new SoapClient('https://UrlToService/Service.svc?wsdl', $options);
try
{
$phpresponse = $client->Get();
print $phpresponse->GetResult->Version;
echo "</b><BR/><BR/>";
}
catch(Exception $e)
{
echo "<h2>Exception Error!</h2></b>";
echo $e->getMessage();
echo "<BR/><BR/>";
}
WCF Configuration
<behavior name="sslBehavior">
<serviceMetadata httpsGetEnabled="true" />
<serviceDebug includeExceptionDetailInFaults="true" />
<serviceCredentials>
<userNameAuthentication userNamePasswordValidationMode="Custom" customUserNamePasswordValidatorType="MyNamespace.ServiceUserNameValidator, MyNamespace" />
</serviceCredentials>
<serviceSecurityAudit
auditLogLocation="Application"
serviceAuthorizationAuditLevel="Failure"
messageAuthenticationAuditLevel="Failure"
suppressAuditFailure="true" />
</behavior>
<customBinding>
<binding name="sslCustomBinding">
<security authenticationMode="UserNameOverTransport" includeTimestamp="true" allowInsecureTransport="true">
<localServiceSettings maxClockSkew="00:10:00" />
<localClientSettings maxClockSkew="00:10:00" />
<secureConversationBootstrap />
</security>
<textMessageEncoding messageVersion="Soap11" />
<httpsT开发者_如何学Goransport />
</binding>
</customBinding>
<service behaviorConfiguration="sslBehavior" name="MyNamespace.Services.Service">
<endpoint address="" binding="customBinding"
bindingConfiguration="sslCustomBinding" contract="MyNamespace.ServiceContracts.IService" />
<host>
<baseAddresses>
<add baseAddress="https://UrlToService/Services/" />
</baseAddresses>
</host>
</service>
I solved the problem. I had to extends the "SoapHeader" class in PHP to make it compliant with the WS-Security standard.
Here is the solution :
PHP Header class
class WsseAuthHeader extends SoapHeader
{
private $wss_ns = 'http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-secext-1.0.xsd';
function __construct($user, $pass, $ns = null)
{
if ($ns)
{
$this->wss_ns = $ns;
}
$auth = new stdClass();
$auth->Username = new SoapVar($user, XSD_STRING, NULL, $this->wss_ns, NULL, $this->wss_ns);
$auth->Password = new SoapVar($pass, XSD_STRING, NULL, $this->wss_ns, NULL, $this->wss_ns);
$username_token = new stdClass();
$username_token->UsernameToken = new SoapVar($auth, SOAP_ENC_OBJECT, NULL, $this->wss_ns, 'UsernameToken', $this->wss_ns);
$security_sv = new SoapVar(
new SoapVar($username_token, SOAP_ENC_OBJECT, NULL, $this->wss_ns, 'UsernameToken', $this->wss_ns),
SOAP_ENC_OBJECT, NULL, $this->wss_ns, 'Security', $this->wss_ns);
parent::__construct($this->wss_ns, 'Security', $security_sv, true);
}
}
PHP Client call
$options = array(
'soap_version' => SOAP_1_1,
'exceptions' => true,
'trace' => 1,
'wdsl_local_copy' => true
);
$username = "MyUser";
$password = "MyPassword";
$wsse_header = new WsseAuthHeader($username, $password);
$client = new SoapClient('https://UrlToService/Service.svc?wsdl', $options);
$client->__setSoapHeaders(array($wsse_header));
try
{
$phpresponse = $client->Get();
print $phpresponse->GetResult->Version;
echo "</b><BR/><BR/>";
}
catch(Exception $e)
{
echo "<h2>Exception Error!</h2></b>";
echo $e->getMessage();
}
Hope it will helps someone else!
Thanks to Chris : Connecting to WS-Security protected Web Service with PHP
SOAP's a bit funny with Authentication (or can be), I have used the method from http://www.php.net/manual/en/soapclient.soapclient.php#101503 see comment from, faebu at faebu dot ch 21-Dec-2010 02:58 for doing exactly this a few times... it may help
Essentially uses CURL to pass the authentication credentials... it may/may not work for you!
精彩评论