Adding basic HTTP auth to a WCF REST service

前端 未结 3 1483
太阳男子
太阳男子 2020-12-02 13:43

I have a WCF HTTP REST Service and I tie into it with an HTTP client in a different programming language who writes its own custom HTTP. I would like to add WWW-Authenticate

相关标签:
3条回答
  • 2020-12-02 14:30

    I've had similar problems and found a lot of different approaches, especially the cross domain calls, together with basic authentication seems to be a bit of a challenge. Jquery for example first issues an OPTIONS call to verify it the POST is allowed. Wcf normally declines this request and you get a strange error.

    I've finally got it working and you can download sample code from my blog: http://sameproblemmorecode.blogspot.com/2011/10/creating-secure-restfull-wcf-service.html

    0 讨论(0)
  • 2020-12-02 14:38

    Just to add to this, Chrome will not load the login dialog unless you change "BasicRealm" to "BasicRealm=site" in the OnEndRequest method:

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

    And thanks, this is such a simple solution.

    0 讨论(0)
  • 2020-12-02 14:44

    I was also interested in custom authentication in a REST HTTP WCF service and finally got it to work.

    That being said my code will give you a way to get it working, but I recommend reading this guide which explains everything in more depth: http://wcfsecurityguide.codeplex.com/

    First, change the system.web portion of your Web.Config to look like this:

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

    Then add another file to your project: 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 讨论(0)
提交回复
热议问题