web-dev-qa-db-fra.com

Authentification de base du service Web Asmx

Je veux implémenter l'authentification de base à l'aide de la validation du nom d'utilisateur et du mot de passe dans mon service Web asmx.
Je ne veux pas utiliser WCF et je sais que ce n'est pas un moyen sûr, mais je dois utiliser l'authentification de base sans utiliser https.

Mon service Web est comme ceci:

[WebService(Namespace = "http://www.mywebsite.com/")]
public class Service1
{
    [WebMethod]
    public string HelloWorld()
    {
        return "Hello world";
    }
}

Et j'utilise ce HttpModule personnalisé:

public class BasicAuthHttpModule : IHttpModule
{
    void IHttpModule.Init(HttpApplication context)
    {
        context.AuthenticateRequest += new EventHandler(OnAuthenticateRequest);
    }

    void OnAuthenticateRequest(object sender, EventArgs e)
    {
        string header = HttpContext.Current.Request.Headers["Authorization"];

        if (header != null && header.StartsWith("Basic"))  //if has header
        {
            string encodedUserPass = header.Substring(6).Trim();  //remove the "Basic"
            Encoding encoding = Encoding.GetEncoding("iso-8859-1");
            string userPass = encoding.GetString(Convert.FromBase64String(encodedUserPass));
            string[] credentials = userPass.Split(':');
            string username = credentials[0];
            string password = credentials[1];

            if(!MyUserValidator.Validate(username, password))
            {
                HttpContext.Current.Response.StatusCode = 401;
                HttpContext.Current.Response.End();
            }
        }
        else
        {
            //send request header for the 1st round
            HttpContext context = HttpContext.Current;
            context.Response.StatusCode = 401;
            context.Response.AddHeader("WWW-Authenticate", String.Format("Basic realm=\"{0}\"", string.Empty));
        }
    }

    void IHttpModule.Dispose()
    {
    }
}

Et dans le web.config j'utilise ceci:

<?xml version="1.0"?>
<configuration>
    <appSettings/>
    <connectionStrings/>
    <system.web>
        <customErrors mode="Off" />
        <compilation debug="true" targetFramework="4.0"/>
        <authentication mode="None"/>
    </system.web>
    <system.webServer>
        <modules runAllManagedModulesForAllRequests="true">
            <add name="BasicAuthHttpModule"
                 type="AuthService.BasicAuthHttpModule, AuthService" />
        </modules>
    </system.webServer>
</configuration>    

Le code appelant est:

static void Main(string[] args)
{
    var proxy = new Service1.Service1()
                    {
                        Credentials = new NetworkCredential("user1", "p@ssw0rd"),
                        PreAuthenticate = true
                    };
    try
    {
        var result = proxy.HelloWorld();
        Console.WriteLine(result);
    }
    catch (Exception e)
    {
        Console.WriteLine(e.Message);
    }
    Console.ReadKey();
}

lorsque j'utilise ce service Web, le service demande une authentification de base mais la variable header dans la méthode OnAuthenticateRequest est toujours nulle et MyUserValidator.Validate() ne s'exécute jamais.

MODIFIER

Les résultats du violoneux:

POST http://www.mywebsite.com/Service1.asmx HTTP/1.1
User-Agent: Mozilla/4.0 (compatible; MSIE 6.0; MS Web Services Client Protocol 2.0.50727.4927)
VsDebuggerCausalityData: uIDPo+drc57U77xGu/ZaOdYvw6IAAAAA8AjKQNpkV06FEWDEs2Oja2C+h3kM7dlDvnFfE1VlIIIACQAA
Content-Type: text/xml; charset=utf-8
SOAPAction: "http://www.mywebsite.com/HelloWorld"
Host: www.mywebsite.com
Content-Length: 291
Expect: 100-continue
Connection: Keep-Alive

<?xml version="1.0" encoding="utf-8"?><soap:Envelope xmlns:soap="http://schemas.xmlsoap.org/soap/envelope/" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" 

xmlns:xsd="http://www.w3.org/2001/XMLSchema"><soap:Body><HelloWorld xmlns="http://www.mywebsite.com/" /></soap:Body></soap:Envelope>
HTTP/1.1 401 Unauthorized
Cache-Control: private
Content-Type: text/html
Server: Microsoft-IIS/7.5
WWW-Authenticate: Basic realm=""
X-AspNet-Version: 4.0.30319
WWW-Authenticate: Basic realm="www.mywebsite.com"
X-Powered-By: ASP.NET
Date: Sun, 03 Jun 2012 07:14:40 GMT
Content-Length: 1293
------------------------------------------------------------------

POST http://www.mywebsite.com/Service1.asmx HTTP/1.1
User-Agent: Mozilla/4.0 (compatible; MSIE 6.0; MS Web Services Client Protocol 2.0.50727.4927)
VsDebuggerCausalityData: uIDPo+drc57U77xGu/ZaOdYvw6IAAAAA8AjKQNpkV06FEWDEs2Oja2C+h3kM7dlDvnFfE1VlIIIACQAA
Content-Type: text/xml; charset=utf-8
SOAPAction: "http://www.mywebsite.com/HelloWorld"
Authorization: Basic dXNlcjE6cEBzc3cwcmQ=
Host: www.mywebsite.com
Content-Length: 291
Expect: 100-continue

<?xml version="1.0" encoding="utf-8"?><soap:Envelope xmlns:soap="http://schemas.xmlsoap.org/soap/envelope/" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" 

xmlns:xsd="http://www.w3.org/2001/XMLSchema"><soap:Body><HelloWorld xmlns="http://www.mywebsite.com/" /></soap:Body></soap:Envelope>
HTTP/1.1 401 Unauthorized
Content-Type: text/html
Server: Microsoft-IIS/7.5
WWW-Authenticate: Basic realm="www.mywebsite.com"
X-Powered-By: ASP.NET
Date: Sun, 03 Jun 2012 07:14:41 GMT
Content-Length: 1293
------------------------------------------------------------------
14
Majid Shamkhani

Modifiez votre code HttpModule personnalisé en ceci:

public class BasicAuthHttpModule : IHttpModule
{
    public void Dispose()
    {
    }

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

    public void OnAuthenticateRequest(object source, EventArgs
                        eventArgs)
    {
        HttpApplication app = (HttpApplication)source;

        string authHeader = app.Request.Headers["Authorization"];
        if (!string.IsNullOrEmpty(authHeader))
        {
            string authStr = app.Request.Headers["Authorization"];

            if (authStr == null || authStr.Length == 0)
            {
                return;
            }

            authStr = authStr.Trim();
            if (authStr.IndexOf("Basic", 0) != 0)
            {
                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];

            if (!MyUserValidator.Validate(username, password))
            {
                DenyAccess(app);
                return;
            }
        }
        else
        {
            app.Response.StatusCode = 401;
            app.Response.End();
        }
    }
    public void OnEndRequest(object source, EventArgs eventArgs)
    {
        if (HttpContext.Current.Response.StatusCode == 401)
        {
            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";
        app.Response.Write("401 Access Denied");
        app.CompleteRequest();
    }
}

Activez ensuite Anonymous authentication et désactivez l'authentification Basic, Digest et Windows pour votre site Web dans IIS.

Remarque: Cette implémentation fonctionnera également avec WCF.

12
Majid Shamkhani

Il semble que vous ayez besoin d'envoyer les en-têtes manuellement la première fois:

de Blog de Rick Strahl

    string url = "http://rasnote/wconnect/admin/wc.wc?_maintain~ShowStatus";
    HttpWebRequest req = HttpWebRequest.Create(url) as HttpWebRequest;

    string user = "ricks";
    string pwd = "secret";
    string domain = "www.west-wind.com";

    string auth = "Basic " + Convert.ToBase64String(System.Text.Encoding.Default.GetBytes(user + ":" + pwd));
    req.PreAuthenticate = true;
    req.Headers.Add("Authorization", auth);
    req.UserAgent = ": Mozilla/5.0 (Windows; U; Windows NT 6.1; en-US; rv:1.9.1.3) Gecko/20090824 Firefox/3.5.3 (.NET CLR 4.0.20506)";
    WebResponse resp = req.GetResponse();
    resp.Close();
1
Preet Sangha