【问题标题】:RestSharp Authenticator Follow 302 RedirectRestSharp Authenticator 跟随 302 重定向
【发布时间】:2016-06-09 19:10:21
【问题描述】:

我正在尝试使用 RestSharp 向 API 发出请求。此 API 通过将请求重定向到登录服务器、使用基本凭据进行身份验证、获取 cookie,然后重定向回 API 来确保安全。恐怕我无法控制这个。

所以请求的顺序是:

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

我正在尝试使用 RestSharp 来做到这一点。这是我的代码:

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;

授权标头仅在第一个请求时发送。它不遵循任何重定向。

有没有办法让 RestSharp 只将凭据发送到登录服务器?

【问题讨论】:

  • 我公司的私有API。
  • 您如何验证基本身份验证标头没有被发送?您确定这是使用基本身份验证吗?如果涉及基于 Web 的表单和 cookie,那么几乎可以肯定它不是基本身份验证。
  • 使用 Fiddler,我可以确认没有发送授权标头。我知道 API 正在使用基本身份验证。我已经构建了其他使用此 API 的非 .NET 应用程序。

标签: c# .net restsharp


【解决方案1】:

它不是一直都在。发布到堆栈溢出后找到解决方案。

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

我必须构建一个自定义 System.Net.IAuthenticationModule,而不是在 RestClient 上使用 IAuthenticator。这是我的解决方案:

我的 RestSharp 身份验证器

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

我的 .NET 身份验证模块

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

像这样添加到 RestSharp 客户端

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

【讨论】:

  • 我在 MyAuthenticationModule.Authenticate 方法上放置了一个断点,但在我的情况下它从未触发过。在我的情况下,登录 url 与 api url 相同,/wp-json/wp/v2/users/me 需要基本身份验证,然后在成功身份验证后将我重定向到 /wp-json/wp/v2/users/2 哪个还需要基本身份验证。
  • vani,您可能需要将 System.Net.CookieContainer 添加到您的 RestClient。结帐github.com/restsharp/RestSharp/wiki/Cookies
  • 但在我的情况下,它现在甚至不验证第一个 url (/wp-json/wp/v2/users/me),现在它在第一个 url 上返回 401。使用基本身份验证器时,它确实验证了第一个 url 和 302 到第二个。在那里它与 403 中断。
  • 啊,好吧。我从来没有在同一个域中尝试过这个。你传递给 MyAuthenticator 构造函数的是什么? loginServerUrl 是否与您传递给 RestClient 构造函数的 URL 相同。
  • 是的,它是同一个网址。在我通过第一个身份验证之前,我不知道第二个 url 是什么。
【解决方案2】:

您可以在 Authenticate 方法中为请求分配一个 CredentialsCache 对象。 将这些凭据传递给请求表明您允许使用它们,即使用于后续请求(重定向)。

来自 msdn article:

重定向时不会从 Credentials 属性中删除 CredentialCache,因为 WebRequest 知道您将允许发送凭据的位置。您还可以通过将缓存分配给后续请求来重用缓存。

因此,RestSharp BasicAuthenticator 实现可能如下所示:

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

【讨论】:

  • 完美运行!
猜你喜欢
  • 2015-04-22
  • 1970-01-01
  • 1970-01-01
  • 2014-11-13
  • 2012-01-05
  • 2020-04-15
  • 1970-01-01
  • 1970-01-01
  • 2023-03-12
相关资源
最近更新 更多