2011-01-17 10 views
9

Ich habe einen WCF-HTTP-REST-Dienst und binde ihn mit einem HTTP-Client in einer anderen Programmiersprache ein, der sein eigenes benutzerdefiniertes HTTP schreibt. Ich möchte WWW-Authenticate grundlegende Authentifizierung Unterstützung zu meinem WCF-Dienst hinzufügen.Einfache HTTP-Authentifizierung zu einem WCF-REST-Service hinzufügen

Meine Methoden wie folgen aussehen:

[WebInvoke(UriTemplate = "widgets", Method = "POST")] 
public XElement CreateWidget(XElement e) 
{ 
... 
} 

Ist es möglich für mich irgendwie eingehende HTTP-Anforderungen zu filtern, um so ich für ein gültiges Grund Auth Zeichenfolge überprüfen kann, bevor er jeden der REST-Methoden wie CreateWidget oben trifft? Hinweis: Meine Authentifizierungsinformationen sind in meiner Datenbank gespeichert.

Grundsätzlich möchte ich dies in den Anforderungsheadern überprüfen: Authorization: Basic QWxhZGRpbjpvcGVuIHNlc2FtZQ== und dann kann ich selbst diese Zeichenfolge analysieren und die u/p in der Datenbank validieren.

Die web.config-Datei ist wie folgt:

<?xml version="1.0"?> 
<configuration> 

    <connectionStrings> 
    <add name="DatabaseConnectionString" connectionString="Data Source=.\SQLEXPRESS;Initial Catalog=Database;Integrated Security=True" providerName="System.Data.SqlClient" /> 
    </connectionStrings> 
    <system.web> 
    <compilation debug="true" targetFramework="4.0" /> 
    <httpRuntime maxRequestLength="10485760" /> 
    </system.web> 

    <system.webServer> 
    <modules runAllManagedModulesForAllRequests="true"> 
     <add name="UrlRoutingModule" type="System.Web.Routing.UrlRoutingModule, System.Web, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a" /> 
    </modules> 
    </system.webServer> 

    <system.serviceModel> 
    <serviceHostingEnvironment aspNetCompatibilityEnabled="true" /> 
    <standardEndpoints> 
     <webHttpEndpoint> 
     <standardEndpoint name="" helpEnabled="true" automaticFormatSelectionEnabled="true" maxReceivedMessageSize="1048576" maxBufferSize="1048576" /> 
     </webHttpEndpoint> 
    </standardEndpoints> 
    </system.serviceModel> 

</configuration> 

Antwort

8

ich in der benutzerdefinierten Authentifizierung in einem Service-REST HTTP WCF auch interessiert war und schließlich bekam sie zu arbeiten.

Dass gesagt wird mein Code gibt Ihnen einen Weg, um es zum Laufen zu bringen, aber ich empfehle dieses Handbuch zu lesen, die alles in mehr Tiefe erklärt: http://wcfsecurityguide.codeplex.com/

zunächst den system.web Teil Ihres Web.Config ändern aussehen dies wie:

<system.web> 
    <compilation debug="true" targetFramework="4.0" /> 
    <httpRuntime maxRequestLength="10485760" /> 
    <authentication mode="None"></authentication> 
    <httpModules> 
    <add name="BasicAuthenticationModule" type="YourNamespace.UserNameAuthenticator" /> 
    </httpModules> 
</system.web> 

Dann eine andere Datei zu Ihrem Projekt hinzufügen: UserNameAuthenticator.cs

using System; 
using System.Collections.Generic; 
using System.Text; 
using System.Web; 
using System.Web.Security; 
using System.Security.Principal; 
using System.ServiceModel.Activation; 

namespace YourNamespace 
{ 
    public class UserNameAuthenticator : IHttpModule 
    { 
     public void Dispose() 
     { 
     } 

     public void Init(HttpApplication application) 
     { 
      application.AuthenticateRequest += new EventHandler(this.OnAuthenticateRequest); 
      application.AuthorizeRequest += new EventHandler(this.OnAuthorizationRequest); 
      application.EndRequest += new EventHandler(this.OnEndRequest); 
     } 

     public bool CustomAuth(string username, string password) 
     { 
      //TODO: Implement your custom auth logic here 
      return true; 
     } 

     public string[] GetCustomRoles(string username) 
     { 
      return new string[] { "read", "write" }; 
     } 

     public void OnAuthorizationRequest(object source, EventArgs eventArgs) 
     { 
      HttpApplication app = (HttpApplication)source; 
      //If you want to handle authorization differently from authentication 
     } 

     public void OnAuthenticateRequest(object source, EventArgs eventArgs) 
     { 
      HttpApplication 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 (authStr == null || authStr.Length == 0) 
       { 
        // No credentials; anonymous request 
        return; 
       } 
       authStr = authStr.Trim(); 
       if (authStr.IndexOf("Basic", 0) != 0) 
       { 
        //header not correct we do not authenticate 
        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 char[] { ':' }); 
       string username = userPass[0]; 
       string password = userPass[1]; 
       //the user is validated against the SqlMemberShipProvider 
       //If it is validated then the roles are retrieved from the 
       //role provider and a generic principal is created 
       //the generic principal is assigned to the user context 
       // of the application 
       if (CustomAuth(username, password)) 
       { 
        string[] roles = GetCustomRoles(username); 
        app.Context.User = new GenericPrincipal(new 
        GenericIdentity(username, "Membership Provider"), roles); 
       } 
       else 
       { 
        DenyAccess(app); 
        return; 
       } 
      } 
      else 
      { 
       //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 
       app.Response.StatusCode = 401; 
       app.Response.End(); 
      } 
     } 

     public void OnEndRequest(object source, EventArgs eventArgs) 
     { 
      if (HttpContext.Current.Response.StatusCode == 401) 
      { 
       //if the status is 401 the WWW-Authenticated is added to 
       //the response so client knows it needs to send credentials 
       HttpContext context = HttpContext.Current; 
       context.Response.StatusCode = 401; 
       context.Response.AddHeader("WWW-Authenticate", "Basic Realm"); 
      } 
     } 
     private void DenyAccess(HttpApplication app) 
     { 
      app.Response.StatusCode = 401; 
      app.Response.StatusDescription = "Access Denied"; 
      // error not authenticated 
      app.Response.Write("401 Access Denied"); 
      app.CompleteRequest(); 
     } 
    } // End Class 
} //End Namespace 
+0

Ich habe die Schritte wie Sie vorgeschlagen, aber das Modul wird nie aufgerufen. Ich habe versucht, Basic Auth auf IIS hinzuzufügen, aber das Modul wird nicht aufgerufen. Fehle ich etwas? –

+0

Ich bin mir nicht sicher, warum es für mich gearbeitet hat, aber vielleicht damit verbunden: http://stackoverflow.com/questions/355261/whats-the-difference-between-system-web-and-system-webserver (ich war testing via VS-Debugging automatisch gehosteten Webserver) –

+0

Ich musste das Modul manuell zu IIS 7.5 hinzufügen. –

3

ich je hatte ähnliche Probleme und fand viele verschiedene Ansätze, vor allem die Cross-Domain-Anrufe, zusammen mit der grundlegenden Authentifizierung scheint eine kleine Herausforderung. Jquery zum Beispiel gibt zuerst einen OPTIONS-Aufruf aus, um zu verifizieren, dass der POST erlaubt ist. Wcf lehnt diese Anfrage normalerweise ab und Sie erhalten einen seltsamen Fehler.

habe ich endlich habe es funktioniert und Sie können Beispielcode aus meinem Blog herunterladen: http://sameproblemmorecode.blogspot.com/2011/10/creating-secure-restfull-wcf-service.html

2

Nur um diese hinzuzufügen, Chrome wird nicht die Login-Dialog geladen werden, wenn Sie „BasicRealm“ auf „BasicRealm = site "in der OnEndRequest-Methode:

public void OnEndRequest(object source, EventArgs eventArgs) 
    { 
     if (HttpContext.Current.Response.StatusCode == 401) 
     { 
      //if the status is 401 the WWW-Authenticated is added to 
      //the response so client knows it needs to send credentials 
      HttpContext context = HttpContext.Current; 
      context.Response.StatusCode = 401; 
      context.Response.AddHeader("WWW-Authenticate", "Basic Realm=site"); 
     } 
    } 

Und danke, das ist so eine einfache Lösung.

Verwandte Themen