如何使用通过 PHP 页面使用自定义用户名验证的 WCF Web 服务?

问题描述:

我很难从 PHP 站点使用安全的 WCF Web 服务.我对 PHP 的了解有限,我在网上找到了各种示例,但还没有成功使它们工作.

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.

我有一个 Silverlight 应用程序,它也使用此 WebService,并且运行良好.但是当我运行 PHP 站点时,出现此错误:

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:安全处理器无法在消息中找到安全标头.这可能是因为消息是不安全的错误,或者因为通信双方之间存在绑定不匹配.如果服务配置为安全且客户端未使用安全,则可能会发生这种情况.

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.

我还尝试将 customBinding 更改为 basicHttpBinding.当我这样做时,不再调用自定义用户名验证器.但是,在我的 WCF 服务上使用 basicHttpBinding 并删除凭据验证有效"(除了它不安全的事实).所以问题似乎与安全消息有关.

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.

谁能给我一个可行的例子或教程来帮助我制作这个 PHP 页面?

Could someone give me a working example or tutorial that could help me making this PHP page works?

这是我的 PHP 代码:

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 配置

<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" />
      <httpsTransport />
    </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>

我解决了这个问题.我必须扩展 PHP 中的SoapHeader"类以使其符合 WS-Security 标准.

I solved the problem. I had to extends the "SoapHeader" class in PHP to make it compliant with the WS-Security standard.

这是解决方案:

PHP 标头类

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 客户端调用

$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(); 
}

希望能帮到别人!

感谢 Chris:连接到受 WS-Security 保护的 Web使用 PHP 服务