开发者

WCF and Authentication

i need to pass a value from client everytime a request sent to WCF and check that value on server and decide to make the request or not, can any one write an example of that ?im not sure how is that is going be implemented

case: im generating a key based on the开发者_如何学Python hardware of the client and i want to send that key to the server with every request to check if the key is accepted in the server db and then decide to process the request or not.

thanks in advance.


You are looking for message inspector. Check this article.

Edit:

Mentioned approach is the easiest one in your case. You will create client message inspector to add custom header and dispatch message inspector to extract header and validate key. If the key is not valid you will throw an exception.

The clean solution is to create custom token and cutom credentials but it is really complex so unless you want to dive deeply into WCF security implementation use message inspectors.


I've implemented something like this to deal with some particularly "custom" authentication where methods are allowed or denied based on database state. It uses PerSession implementation, which allows you to avoid passing that key every time, since you can maintain the proxy for the duration of execution. It also provides some performance benefits by eliminating the overhead of instantiating the proxy repeatedly (may not be an issue depending on your design).

  1. Implement your service with an InstanceContextMode of "PerSession" [ServiceBehavior(InstanceContextMode = InstanceContextMode.PerSession)]
  2. Provide an initialization method in your service which accepts the key, and keeps a reference to it.
  3. In your service's methods, check that key, other conditions, etc. to determine what you need to do.
  4. Now when you instantiate the client proxy, call that initialization method first passing the key, and from that point forward you can proceed to make calls without it.


first we need to implement Behavior and inspector in the client side to send Key to authenticate the client :

 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)
    {

    }
}

now we need to implement Behavior and inspector in the server side (WCF service) to inspect every request made and extract the header then validate it :

  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)
    {

    }
}

now lets register the behavior :

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

that is it thanks.


I came to this article when trying to implement an authentication mechanism on a WCF Rest service, I was trying to obtain the Authentication Header on the custom message inspector method AfterReceiveRequest but had issues retrieving the headers with the provided System.ServiceModel.Channel.Message object (request var on the method signature)

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

The headerIndex would be always -1, given this I researched and found that there is an extension method to the Message class which allows to convert the Message to an object of type System.Net.Http.HttpRequestMessage. To do so all it is needed is to import the following assemblies: 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
0

上一篇:

下一篇:

精彩评论

暂无评论...
验证码 换一张
取 消

最新问答

问答排行榜