RestSharp Authenticator Follow 302 Redirect

我只是一个虾纸丫 提交于 2019-12-18 05:07:11

问题


I am trying to make requests to an API with RestSharp. This API is secured by redirecting the request to a login server, authenticate with basic credentials, obtain cookies, then redirect back to the API. I am afraid i have no control over the this.

So the sequence of requests is:

Request                                            Response
---------------------------------------------------------------------------------
1. GET http api server                             302 Found to login server
2. GET https login server                          401 Unauthorized
3. GET https login server with basic credentials   302 Found to api server with cookies
4. GET http api server with cookies                200 OK

I am trying to do this with RestSharp. Here is my code:

var client = new RestClient("api server")
{
    Authenticator = new HttpBasicAuthenticator("username", "password")
};
var request = new RestRequest("api path", Method.GET);
var result = client.Execute<TResult>(request).Data;

The authorization header is only sent on the first request. It does not follow any of the redirects.

Is there a way i can make RestSharp send the credentials to the login server only?


回答1:


Doesn't it always go. You find the solution after you post to stack overflow.

https://github.com/restsharp/RestSharp/issues/414

Instead of using an IAuthenticator on the RestClient, I have to build a custom System.Net.IAuthenticationModule. Here is my solution:

My RestSharp Authenticator

public class MyAuthenticator : IAuthenticator
{
    private readonly CredentialCache _credentials = new CredentialCache();

    public MyAuthenticator(Uri loginServerUrl, string username, string password)
    {
        if (loginServerUrl == null)
        {
            throw new ArgumentNullException("loginServerUrl");
        }

        __registerAuthenticationModule(loginServerUrl);
        _credentials.Add(loginServerUrl, MyAuthenticationModule.TheAuthenticationType, new NetworkCredential(username, password, loginServerUrl.Host));
    }

    private static MyAuthenticationModule __registerAuthenticationModule(Uri loginServerUrl)
    {
        IEnumerator registeredModules = AuthenticationManager.RegisteredModules;
        MyAuthenticationModule authenticationModule;

        while (registeredModules.MoveNext())
        {
            object current = registeredModules.Current;
            if (current is MyAuthenticationModule)
            {
                authenticationModule = (MyAuthenticationModule)current;
                if (authenticationModule.LoginServerUrl.Equals(loginServerUrl))
                {
                    return authenticationModule;
                }
            }
        }

        authenticationModule = new MyAuthenticationModule(loginServerUrl);
        AuthenticationManager.Register(authenticationModule);
        return authenticationModule;
    }

    public void Authenticate(IRestClient client, IRestRequest request)
    {
        request.Credentials = _credentials;
    }
}

My .NET Authentication Module

public class MyAuthenticationModule : IAuthenticationModule
{
    internal const string TheAuthenticationType = "MyAuthentication";

    private readonly CredentialCache _credentialCache = new CredentialCache();
    private readonly Uri _loginServerUrl;

    internal CredentialCache CredentialCache
    {
        get
        {
            return _credentialCache;
        }
    }

    internal Uri LoginServerUrl
    {
        get
        {
            return _loginServerUrl;
        }
    }

    internal MyAuthenticationModule(Uri loginServerurl)
    {
        if (loginServerurl == null)
        {
            throw new ArgumentNullException("loginServerUrl");
        }

        _loginServerUrl = loginServerurl;
    }

    public Authorization Authenticate(string challenge, WebRequest request, ICredentials credentials)
    {
        Authorization result = null;

        if (request == null || credentials == null)
        {
            result = null;
        }

        else
        {
            NetworkCredential creds = credentials.GetCredential(LoginServerUrl, TheAuthenticationType);
            if (creds == null)
            {
                return null;
            }

            ICredentialPolicy policy = AuthenticationManager.CredentialPolicy;
            if (policy != null && !policy.ShouldSendCredential(LoginServerUrl, request, creds, this))
            {
                return null;
            }

            string token = Convert.ToBase64String(Encoding.UTF8.GetBytes(string.Format("{0}:{1}", creds.UserName, creds.Password)));

            result = new Authorization(string.Format("Basic {0}", token));
        }

        return result;
    }

    public string AuthenticationType
    {
        get { return TheAuthenticationType; }
    }

    public bool CanPreAuthenticate
    {
        get { return false; }
    }

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

Add to RestSharp Client Like This

var client = new RestClient(commonApiUrl)
{
    Authenticator = new MyAuthenticator(loginServerUrl, username, password)
};



回答2:


You can just assign a CredentialsCache object to the request in the Authenticate method. Passing these credentials to a request indicates to the request that you allow them to be used, even for subsequent requests (redirects).

From this msdn article:

A CredentialCache will not be removed from the Credentials property when redirecting because WebRequest knows where you will allow your credentials sent. You may also reuse your cache by assigning it to subsequent requests.

So a RestSharp BasicAuthenticator implementation could look like this:

 public class BasicAuthenticator : IAuthenticator
    {
        private readonly string _baseUrl;
        private readonly string _userName;
        private readonly string _password;
        private readonly CredentialCache _credentialCache;

        public BasicAuthenticator(string baseUrl, string userName, string password)
        {
            _baseUrl = baseUrl;
            _userName = userName;
            _password = password;

            _credentialCache = new CredentialCache
            {
                {new Uri(_baseUrl), "Basic", new NetworkCredential(_userName, _password)}
            };
        }

        public void Authenticate(IRestClient client, IRestRequest request)
        {
            request.Credentials = _credentialCache;

            if (request.Parameters.Any(parameter =>
                            parameter.Name.Equals("Authorization", StringComparison.OrdinalIgnoreCase)))
            {
                return;
            }
            request.AddParameter("Authorization", GetBasicAuthHeaderValue(), ParameterType.HttpHeader);
        }


        private string GetBasicAuthHeaderValue()
        {
            return string.Format("Basic {0}",
                            Convert.ToBase64String(Encoding.ASCII.GetBytes(string.Format("{0}:{1}",
                                _userName, _password))));
        }
    }


来源:https://stackoverflow.com/questions/28195208/restsharp-authenticator-follow-302-redirect

标签
易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!