2011-01-17 44 views
9

Tengo un WCF HTTP REST Service y me conecto con un cliente HTTP en un lenguaje de programación diferente que escribe su propio HTTP personalizado. Me gustaría agregar WWW-Authenticate soporte de autenticación básica para mi servicio WCF.Agregar HTTP auth básico a un servicio WCF REST

Mis métodos se ven así:

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

¿Es posible para mí para filtrar alguna manera las peticiones HTTP para que pueda comprobar si hay una cadena de autenticación de base en vigor antes de que llegue a cada uno de los métodos REST como CreateWidget arriba? Nota: Mi información de autenticación está en mi base de datos.

Básicamente quiero comprobar esto en los encabezados de solicitud: Authorization: Basic QWxhZGRpbjpvcGVuIHNlc2FtZQ== y luego puedo analizar la cadena y validar el u/p en la base de datos.

El archivo web.config es el siguiente:

<?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> 

Respuesta

8

Yo también estaba interesado en la autenticación personalizada en un servicio REST HTTP de WCF y, finalmente, tengo que trabajar.

Dicho mi código le dará una manera de conseguir que funcione, pero recomiendo la lectura de esta guía que explica todo en mayor profundidad: http://wcfsecurityguide.codeplex.com/

En primer lugar, cambiar la parte de su system.web Web.Config para mirar de esta manera:

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

Luego agregar otro archivo a su proyecto: 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

He seguido los pasos que me sugirió, pero nunca se llama al módulo. Traté de agregar Basic Auth en IIS, pero aún así, el módulo no se llama. ¿Me estoy perdiendo de algo? –

+0

No estoy seguro de por qué, ya que funcionó para mí, pero tal vez relacionado con esto: http://stackoverflow.com/questions/355261/whats-the-difference-between-system-web-and-system-webserver (I was prueba a través de VS depuración de servidor web alojado automáticamente) –

+0

Tuve que agregar el módulo manualmente a IIS 7.5. –

3

que he tenido problemas similares y encontraron una gran cantidad de enfoques diferentes, especialmente las llamadas de dominio cruzado, junto con la autenticación básica parece ser un poco un desafío. Jquery, por ejemplo, primero emite una llamada OPTIONS para verificar que el POST está permitido. Wcf normalmente rechaza esta solicitud y se obtiene un error extraño.

fin tengo que trabajar y se puede descargar el código de ejemplo de mi blog: http://sameproblemmorecode.blogspot.com/2011/10/creating-secure-restfull-wcf-service.html

2

sólo para añadir a esto, Chrome no se carga la ventana de acceso a menos que cambie "BasicRealm" a "= BasicRealm sitio "en el método OnEndRequest:

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"); 
     } 
    } 

Y gracias, esta es una solución tan simple.

Cuestiones relacionadas