2010-09-02 178 views
6

我每次发送到WCF的请求都需要传递一个值,并检查服务器上的值并决定是否发出请求,任何人都可以写一个例子吗?我不知道那是怎么回事即将实施WCF和身份验证

case: 即时生成基于客户端硬件的密钥,我想发送该密钥到服务器与每个请求,以检查密钥是否被接受在服务器数据库然后决定处理请求与否。

在此先感谢。

+1

参见http://stackoverflow.com/questions/964433/how-to-add-a-custom-header-to-every-wcf-calls自动传递数据作为报头。不过,我不知道如何在服务器上自动检查它,但我希望有一个钩子。 – Rup 2010-09-02 08:37:24

回答

1

首先我们需要实现在客户端行为,并检查送钥匙到客户端进行身份验证:

class AuthenticationBehaviour : IEndpointBehavior 
{ 
    #region IEndpointBehavior Members 

    public void AddBindingParameters(ServiceEndpoint endpoint, System.ServiceModel.Channels.BindingParameterCollection bindingParameters) 
    { 
    } 

    public void ApplyClientBehavior(ServiceEndpoint endpoint, System.ServiceModel.Dispatcher.ClientRuntime clientRuntime) 
    { 
     AuthenticationMessageInspector inspector = new AuthenticationMessageInspector(); 
     clientRuntime.MessageInspectors.Add(inspector); 
    } 

    public void ApplyDispatchBehavior(ServiceEndpoint endpoint, System.ServiceModel.Dispatcher.EndpointDispatcher endpointDispatcher) 
    { 
     //AuthenticationMessageInspector inspector = new AuthenticationMessageInspector(); 
     //endpointDispatcher.DispatchRuntime.MessageInspectors.Add(inspector); 
    } 

    public void Validate(ServiceEndpoint endpoint) 
    { 
    } 

    class AuthenticationMessageInspector : IClientMessageInspector 
{ 
    private const string HeaderKey = "Authentication"; 

    public object BeforeSendRequest(ref Message request, IClientChannel channel) 
    { 

     if (Session.MachineId == 0) 
     { 
      Session.MachineId = LicenseGenerator.GenerateLicense(); 
     } 


     request.Headers.Add(MessageHeader.CreateHeader(HeaderKey, string.Empty, Session.MachineId)); 
     return null; 
    } 

    public void AfterReceiveReply(ref Message reply, object correlationState) 
    { 

    } 
} 

现在我们需要实现在服务器端(WCF服务)的行为和督察检查的每个请求,并提取头然后验证它:

public class AuthenticationBehaviour : IEndpointBehavior 
{ 
    #region IEndpointBehavior Members 

    public void AddBindingParameters(ServiceEndpoint endpoint, System.ServiceModel.Channels.BindingParameterCollection bindingParameters) 
    { 
    } 

    public void ApplyClientBehavior(ServiceEndpoint endpoint, System.ServiceModel.Dispatcher.ClientRuntime clientRuntime) 
    { 
     //AuthenticationMessageInspector inspector = new AuthenticationMessageInspector(); 
     //clientRuntime.MessageInspectors.Add(inspector); 
    } 

    public void ApplyDispatchBehavior(ServiceEndpoint endpoint, System.ServiceModel.Dispatcher.EndpointDispatcher endpointDispatcher) 
    { 
     AuthenticationMessageInspector inspector = new AuthenticationMessageInspector(); 
     endpointDispatcher.DispatchRuntime.MessageInspectors.Add(inspector); 
     //Console.WriteLine("Dispatcher Applied!"); 
    } 

    public void Validate(ServiceEndpoint endpoint) 
    { 
    } 

    #endregion 
} 

    public class AuthenticationMessageInspector : IDispatchMessageInspector 

{ 

    #region Members 
    private string conStr = "", commStr = ""; 
    public IDbConnection Connection { get; set; } 
    public IDbCommand Command { get; set; } 
    public IDataReader Reader { get; set; } 

    #endregion   
    private const string HeaderKey = "Authentication"; 
    public object AfterReceiveRequest(ref Message request, IClientChannel channel, InstanceContext instanceContext) 
    { 
     //Console.WriteLine("recieved Request! "); 
     int headerIndex = request.Headers.FindHeader(HeaderKey, string.Empty); 
     if (headerIndex < 0 || string.IsNullOrEmpty(request.Headers.GetHeader<String>(headerIndex))) 
     { 

      throw (new Exception("Access Denied!\n")); 
      return null; 
     } 


     bool valid = Validate(request.Headers.GetHeader<String>(headerIndex)); 
     if (!valid) 
     { 
      Console.WriteLine("recieved Request! From " + request.Headers.GetHeader<String>(headerIndex) + " and Access Denied!\n"); 
      throw (new Exception("Access Denied!\n" + request.Headers.GetHeader<String>(headerIndex) + " License Number is not athourized! "));   
     } 
     if (headerIndex != -1) 
     { 
      Console.WriteLine("recieved Request! From " + request.Headers.GetHeader<String>(headerIndex)); 
     } 
     return null; 

    } 

    public void BeforeSendReply(ref Message reply, object correlationState) 
    { 

    } 
} 

现在允许登记的行为:

  _routingHost.Description.Endpoints[0].Behaviors.Add(new Gateway.Controllers.AuthenticationBehaviour()); 
_routingHost.Open(); 

这是它的感谢。

2

您正在寻找讯息检查员。检查这个article

编辑:

提到的方法是你的情况中最容易的方法。您将创建客户端消息检查器以添加自定义标头并分派消息检查器来提取标头和验证密钥。如果密钥无效,则会抛出异常。

干净的解决方案是创建custom tokencutom credentials,但它非常复杂,因此除非您想深入了解WCF安全实现使用消息检查器。

+0

im生成一个基于客户端的硬件的密钥,我想发送该密钥到服务器与每个请求,以检查密钥是否被接受在服务器数据库,然后决定处理请求或不 – Stacker 2010-09-02 09:34:09

+0

所以它是一个身份验证(只需检查具有密钥的客户端是否可以访问服务)或授权(不同的密钥可以使用不同的操作集)? – 2010-09-02 09:58:27

+0

只需检查具有密钥的客户端是否可以访问服务 – Stacker 2010-09-02 10:17:36

1

我已经实现了这样的事情来处理一些特别的“自定义”身份验证,其中基于数据库状态允许或拒绝方法。它使用PerSession实现,它允许您避免每次都传递该密钥,因为您可以在执行期间维护代理。它还通过消除重复实例化代理的开销(可能不是问题取决于您的设计)来提供一些性能优势。

  1. 用“PerSession” [ServiceBehavior(InstanceContextMode = InstanceContextMode.PerSession)]的InstanceContextMode
  2. 请提供服务的初始化方法,该方法接受键,并保持对它的引用实施您服务。
  3. 在您的服务方法中,检查该密钥,其他条件等以确定您需要执行的操作。
  4. 现在,当您实例化客户端代理时,首先调用该初始化方法传递该密钥,然后从该点开始,您可以继续在没有它的情况下进行调用。
+0

其实我已经实现它了,非常感谢你帮助我,我用了一种不同的方法,因为我很快发布了一个答案 – Stacker 2010-09-07 14:52:24

0

我在试图在WCF Rest服务上实现身份验证机制时谈到了这篇文章,我试图在自定义消息检查器方法AfterReceiveRequest上获取身份验证头,但遇到了使用提供的System.ServiceModel检索头的问题。渠道。消息对象(在方法签名请求VAR)

public object AfterReceiveRequest(ref Message request, IClientChannel channel, 
    InstanceContext instanceContext) 
    Dim headerIndex = request.Headers.FindHeader(HeaderKey, String.Empty) 

的headerIndex将总是-1,考虑到本我研究,发现有一个扩展方法的消息类,其允许消息转换为对象类型System.Net.Http.HttpRequestMessage。要做到这一点,所有需要的是导入下列程序集:System.ServiceModel.Channel,System.Net.Http。

Dim httpReq As System.Net.Http.HttpRequestMessage = request.ToHttpRequestMessage() 

Dim authValue As String 
If httpReq.Headers.Contains(HeaderKey) Then 
    authValue = httpReq.Headers.GetValues(HeaderKey)(0) 
End If