How to handle authenticatication with HttpWebReque

2019-04-20 04:31发布

According to MSDN, when HttpWebRequest.AllowAutoRedirect property is true, redirects will clear authentication headers. The workaround given is to implement IAuthenticationModule to handle authentication:

The Authorization header is cleared on auto-redirects and HttpWebRequest automatically tries to re-authenticate to the redirected location. In practice, this means that an application can't put custom authentication information into the Authorization header if it is possible to encounter redirection. Instead, the application must implement and register a custom authentication module. The System.Net.AuthenticationManager and related class are used to implement a custom authentication module. The AuthenticationManager.Register method registers a custom authentication module.

I created a basic implementation of this interface:

public class CustomBasic : IAuthenticationModule
{
    public CustomBasic() { }

    public string AuthenticationType { get { return "Basic"; } }

    public bool CanPreAuthenticate { get { return true; } }

    private bool checkChallenge(string challenge, string domain)
    {
        if (challenge.IndexOf("Basic", StringComparison.InvariantCultureIgnoreCase) == -1) { return false; }
        if (!string.IsNullOrEmpty(domain) && challenge.IndexOf(domain, StringComparison.InvariantCultureIgnoreCase) == -1) { return false; }
        return true;
    }

    public Authorization PreAuthenticate(WebRequest request, ICredentials credentials)
    {
        return authenticate(request, credentials);
    }

    public Authorization Authenticate(String challenge, WebRequest request, ICredentials credentials)
    {
        if (!checkChallenge(challenge, string.Empty)) { return null; }
        return this.authenticate(request, credentials);
    }

    private Authorization authenticate(WebRequest webRequest, ICredentials credentials)
    {
        NetworkCredential requestCredentials = credentials.GetCredential(webRequest.RequestUri, this.AuthenticationType);
        return (new Authorization(string.Format("{0} {1}", this.AuthenticationType, Convert.ToBase64String(Encoding.ASCII.GetBytes(string.Format("{0}:{1}", requestCredentials.UserName, requestCredentials.Password))))));
    }
}

and a simple driver to exercise the functionality:

public class Program
{
    static void Main(string[] args)
    {
        // replaces the existing handler for Basic authentication
        AuthenticationManager.Register(new CustomBasic());
        // make a request that requires authentication
        HttpWebRequest request = (HttpWebRequest)WebRequest.Create(@"https://www.SomeUrlThatRequiresAuthentication.com");
        request.Method = "GET";
        request.KeepAlive = false;
        request.ContentType = "text/plain";
        request.AllowAutoRedirect = true;
        request.Credentials = new NetworkCredential("userName", "password");
        HttpWebResponse result = (HttpWebResponse)request.GetResponse();
    }
}

When I make a request that doesn't redirect, the Authenticate method on my class is called, and authentication succeeds. When I make a request that reutrns a 307 (temporary redirect) response, no methods of my class are called, and authentication fails. What's going on here?

I'd rather not disable auto redirect and write custom logic to handle 3xx responses myself. How can I get my authentication logic to work with auto redirect?

4条回答
ゆ 、 Hurt°
2楼-- · 2019-04-20 05:01

In place of the NetworkCredential, you should pass a CredentialCache for the request.Credentials.

CredentialCache cache = new CredentialCache();
cache.Add(new Uri(@"https://www.SomeUrlThatRequiresAuthentication.com", "Basic", new NetworkCredential("username", "password"));
request.Credentials = cache;

According to the MSDN documentation:

The CredentialCache class stores credentials for multiple Internet resources. Applications that need to access multiple resources can store the credentials for those resources in a CredentialCache instance that then provides the proper set of credentials to the Internet resource when required. When the GetCredential method is called, it compares the Uniform Resource Identifier (URI) and authentication type provided with those stored in the cache and returns the first set of credentials that match.

查看更多
【Aperson】
3楼-- · 2019-04-20 05:04

I hope following would be another option that i took from code project url http://www.codeproject.com/Articles/49243/Handling-Cookies-with-Redirects-and-HttpWebRequest

    String targetUrl = "https://www.SomeUrlThatRequiresAuthentication.com";

    HttpWebRequest request = GetNewRequest(targetUrl);
    HttpWebResponse response = (HttpWebResponse)request.GetResponse();

    while (response.StatusCode ==  HttpStatusCode.MovedPermanently)
    {
        response.Close();
        request = GetNewRequest(response.Headers["Location"]);
        response = (HttpWebResponse)request.GetResponse();
    }


private static HttpWebRequest GetNewRequest(string targetUrl)
{

    HttpWebRequest request = (HttpWebRequest)HttpWebRequest.Create(targetUrl);
    request.AllowAutoRedirect = false;
    request.Headers.Add("Authorization", "Basic xxxxxxxx");
    return request;
}
查看更多
Summer. ? 凉城
4楼-- · 2019-04-20 05:04

Even though the OP is very old, I would nominate Crater's response as THE ANSWER. I went through similar gyrations, including creating a custom authentication module even though the web resource I was accessing only used Basic authentication. What I found was that only after I used a CredentialCache, instead of a simple NetworkCredential, did my authentication module get called after the redirect.

Furthermore, I found that since the authentication I needed was Basic, by merely supplying the CredentialCache I did not need the custom authentication module at all -- the standard Basic module worked just fine.

The following resource seemed to confirm this (in contrast to the .NET documentation reference mentioned in the OP):

https://blogs.msdn.microsoft.com/ncl/2009/05/05/custom-http-authentication-schemes/

查看更多
等我变得足够好
5楼-- · 2019-04-20 05:16

What you need to do is likely a POST request. You are posting variables to authenticate, thus you need to use the POST action.

See this post for more information: Login to website, via C#

Auto redirect shouldn't get in the way of this post request. I'd recommend installing Fiddler and logging in manually, and then watching what happens.

*Keep in mind, when sending a POST request, if there is a login form, you are sending the POST request to the action='/some-url-or-whatever.php' tag in the form. POST the data to that, and you should be able to log in just fine.

Let me know if this helps.

查看更多
登录 后发表回答