首页 文章

将自定义邮件头添加到从Windows Mobile 6客户端使用的WCF服务

提问于
浏览
2

我有一个WCF服务应用程序,它服务于不同类型的客户端 . 在调用服务方法时,我想在服务头中发送一些特定信息 .

使用较新版本的.NET Framework时,我可以使用MessageHeader处理这种情况 . 由于消费者可以将服务视为WCF服务,因此没有问题 .

[DataContract]
public class AuthToken
{
    [DataMember]
    public string Username { get; set; }
    [DataMember]
    public string Password { get; set; }
}

客户端:

AuthWCFSvc.Service1Client client = new AuthWCFSvc.Service1Client();
using (OperationContextScope scope = new OperationContextScope(client.InnerChannel))
{
    SvcAuthClient.AuthWCFSvc.AuthToken token = new AuthWCFSvc.AuthToken();
    token.Username = "wcfuser";
    token.Password = "wcfpass";

    MessageHeader<SvcAuthClient.AuthWCFSvc.AuthToken> header = new MessageHeader<SvcAuthClient.AuthWCFSvc.AuthToken>(token);
    var untyped = header.GetUntypedHeader("Identity", "http://www.my-website.com");
    OperationContext.Current.OutgoingMessageHeaders.Add(untyped);

    client.TestHeader();
}

服务器端 :

MessageHeaders headers = OperationContext.Current.IncomingMessageHeaders;
AuthToken token = headers.GetHeader<AuthToken>("Identity", "http://www.my-website.com");

但是有一些Windows Mobile 6设备与.NET Framework 3.5 Compact Edition一样,也使用这些服务 . 由于技术限制,它们只能将WCF服务作为Web服务进行处理 .

如果客户端正在使用WCF服务作为Web服务,那么如何在服务方法中添加特定的头信息并解析头信息?

1 回答

  • 0

    如您所知,在.NET CF 3.5上,您只能使用WCF作为SOAP方式的标准Web服务 . 因此,您不能使用任何WCF本机安全资源 .

    我想出了如何使用Basic Http Authentication,配置客户端和服务器端,我可以解释如下:


    客户端

    在客户端(在您的设备上使用.Net CF 3.5),它很容易 . 只需通过以下方式通知您的凭据配置您的clientServiceProxy:

    var service = new YourServiceNamespace.YourService();
     service.Credentials = new NetworkCredential("login", "12345");
     service.PreAuthenticate = true;
    

    这将使您的客户端处理来自服务器响应的“WWW-Authenticate”标头,并通过响应标头“Authorization:Basic”自动传递您的凭据 .


    服务器端

    在web.config上的WCF配置中,您应该仅为Transport配置安全性,并使用HTTPS(这足以保护您的消息免受嗅探器的攻击) .

    <basicHttpBinding>
        <binding>
          <security mode="Transport">
            <transport clientCredentialType="None" />
          </security>
        </binding>
      </basicHttpBinding>
    

    现在,由于WCF没有Basic Http Authentication的本机支持,我们必须使用自定义HTTP模块来处理它 .

    public class BasicHttpAuthentication : IHttpModule
    {
        public delegate bool AuthenticateDelegate( string username, string password );
    
        public static AuthenticateDelegate AuthenticateMethod;
    
        public void Dispose() { }
    
        public void Init( HttpApplication application )
        {
            application.AuthenticateRequest += this.OnAuthenticateRequest;
            application.EndRequest += this.OnEndRequest;
        }
    
        private void DenyAccess( HttpApplication app )
        {
            app.Response.StatusCode = 401;
            app.Response.StatusDescription = "Access Denied";
    
            // Write to response stream as well, to give user visual 
            // indication of error during development
            app.Response.Write( "401 Access Denied" );
    
            app.CompleteRequest();
        }
    
        private void OnAuthenticateRequest( object source, EventArgs eventArgs )
        {
            if ( AuthenticateMethod == null )
                return;
    
            var app = ( HttpApplication )source;
    
            //the Authorization header is checked if present
            string authHeader = app.Request.Headers["Authorization"];
            if ( !string.IsNullOrEmpty( authHeader ) )
            {
                string authStr = app.Request.Headers["Authorization"];
    
                if ( string.IsNullOrEmpty( authStr ) )
                    return; // No credentials; anonymous request
    
                authStr = authStr.Trim();
                if ( authStr.IndexOf( "Basic", 0 ) != 0 )
                    // header is not correct...we'll pass it along and 
                    // assume someone else will handle it
                    return;
    
                authStr = authStr.Trim();
    
                string encodedCredentials = authStr.Substring( 6 );
    
                byte[] decodedBytes = Convert.FromBase64String( encodedCredentials );
                string s = new ASCIIEncoding().GetString( decodedBytes );
    
                string[] userPass = s.Split( new[] { ':' } );
                string username = userPass[0];
                string password = userPass[1];
    
                if ( !AuthenticateMethod( username, password ) )
                    this.DenyAccess( app );
            }
            else
            {
                app.Response.StatusCode = 401;
                app.Response.End();
            }
        }
    
        private void OnEndRequest( object source, EventArgs eventArgs )
        {
            //the authorization header is not present
            //the status of response is set to 401 and it ended
            //the end request will check if it is 401 and add
            //the authentication header so the client knows
            //it needs to send credentials to authenticate
            if ( HttpContext.Current.Response.StatusCode == 401 )
            {
                HttpContext context = HttpContext.Current;
                context.Response.StatusCode = 401;
                context.Response.AddHeader( "WWW-Authenticate", "Basic Realm=\"Please inform your credentials\"" );
            }
        }
    }
    

    要启用HTTP模块,请将以下内容添加到system.webServer部分中的web.config文件中:

    <system.webServer>
        <modules>
          <add name="BasicHttpAuthentication" 
            type="BasicHttpAuthentication, YourAssemblyName"/>
        </modules>
    

    现在,您必须通知模块一个函数,用于验证来自客户端的凭据 . 您可以看到模块中有一个名为“AuthenticateMethod”的静态委托,因此您可以在global.asax的Application_Start上通知函数:

    BasicHttpAuthentication.AuthenticateMethod = ( username, password ) => username == "login" && password == "12345";
    

相关问题