401使用WebRequest对象调用Stormpath REST API时未经授权

本文关键字:API 授权 REST Stormpath 使用 WebRequest 对象 调用 | 更新日期: 2023-09-27 18:20:37

我正在使用Stormpath作为身份验证服务。我使用HttpWebRequest调用Stormpath的RestAPI。

我也在使用HttpWebRequest来调用RestAPI,但它不起作用。

private void BtnGetResetApiClick(object sender, EventArgs e)
{
var username = "aaaa";
var password = "bbbb";
ServicePointManager.ServerCertificateValidationCallback = Callback;
var request = WebRequest.Create("https://api.stormpath.com/v1/tenants/current") as HttpWebRequest;
request.UserAgent = ".NET SDK";
request.Method = "GET";
request.Accept = "*/*";
var data = string.Format("{0}:{1}", username, HttpUtility.HtmlEncode(password));
var token = Convert.ToBase64String(Encoding.UTF8.GetBytes(data));
string authHeader = string.Format("Basic {0}", token);
request.Headers.Add("Authorization", authHeader);
request.ServerCertificateValidationCallback = Callback;
using (var response = request.GetResponse())
{
    var stream = response.GetResponseStream();
    if (stream != null)
    {
        var streamReader = new StreamReader(stream);
        var str = streamReader.ReadToEnd();
        streamReader.Close();
        stream.Close();
    }
  }
}
private bool Callback(object obj, X509Certificate certificate, X509Chain  chain, SslPolicyErrors errors)
{
 return true;
}

呼叫时:

var response = request.GetResponse()

我有一个例外:

System.dll中发生类型为"System.Net.WebException"的未经处理的异常。远程服务器返回错误:(401)未经授权。

你能帮我看看我的代码是否有问题吗?

401使用WebRequest对象调用Stormpath REST API时未经授权

更新-使用SDK,它更容易

如果您经常从C#调用Stormpath API,请不要手动编写请求。请改用Stormpath.NET SDK。我是作者。:)

从Package Manager控制台使用install-package Stormpath.SDK进行安装。然后,创建一个IClient对象:

// In a production environment, you'll want to load these from
// environment variables or a secure file, instead of hardcoding!
var apiKey = ClientApiKeys.Builder()
                 .SetId("Your_Stormpath_API_key_ID")
                 .SetSecret("Your_Stormpath_API_key_secret")
                 .Build();
var client = Clients.Builder()
                 .SetApiKey(apiKey)
                 .Build();

获取租户信息现在只是一个简单的调用:

var tenant = await client.GetCurrentTenantAsync();
Console.WriteLine($"Current tenant is: {tenant.Name}");

如果你真的想发出原始请求,你仍然可以这样做!我将在下面解释。


构造Authorization标头

401 Unauthorized响应表示API无法在您的请求中找到有效的授权标头。要正确进行身份验证,您需要两件事:

  • 格式为apiKeyID:apiKeySecret的授权有效载荷
  • 值为Basic base64(payload)Authorization标头

以下是如何构建完整的标头:

// API_KEY_ID and API_KEY_SECRET populated elsewhere
var authPayload = string.Format("{0}:{1}", API_KEY_ID, API_KEY_SECRET);
var authPayloadEncoded = Convert.ToBase64String(Encoding.UTF8.GetBytes(authPayload));
request.Headers.Add(HttpRequestHeader.Authorization, "Basic " + authPayloadEncoded);

你不需要ServerCertificateValidationCallback = Callback的东西。通过上面的头,API将把请求视为有效请求(当然,假设API密钥ID和Secret是正确的)。


重定向处理

需要注意的一点是(一开始我被绊倒了!)WebRequest将自动遵循HTTP 302重定向,但不会将现有标头应用于新请求。

解决方案是禁用以下重定向:

request.AllowAutoRedirect = false;

这意味着您必须自己处理302个响应,但这是将Authorization标头正确应用于每个请求的唯一方法。


工作示例

我根据这个要点创建了一个简单的工作示例。由于我将多次创建请求,我编写了一个助手函数:

private static HttpWebRequest BuildRequest(string method, string uri)
{
    var request = WebRequest.Create(uri) as HttpWebRequest;
    request.UserAgent = "dotnet/csharp web-request";
    request.Method = method;
    request.ContentType = "application/json";
    // Important, otherwise the WebRequest will try to auto-follow
    // 302 redirects without applying the authorization header to the
    // subsequent requests.
    request.AllowAutoRedirect = false;
    // Construct HTTP Basic authorization header
    var authPayload = string.Format("{0}:{1}", API_KEY_ID, API_KEY_SECRET);
    var authPayloadEncoded = Convert.ToBase64String(Encoding.UTF8.GetBytes(authPayload));
    request.Headers.Add(HttpRequestHeader.Authorization, "Basic " + authPayloadEncoded);
    return request;
}

以及一个简单的控制台应用程序,演示如何获取当前租户的URL和名称:

// Get these from the Stormpath admin console
private static string API_KEY_ID = "Your_Stormpath_API_key_ID";
private static string API_KEY_SECRET = "Your_Stormpath_API_key_secret";
static void Main(string[] args)
{
    // First, we need to get the current tenant's actual URL
    string tenantUrl = null;
    var getCurrentTenantRequest = BuildRequest("GET", "https://api.stormpath.com/v1/tenants/current");
    try
    {
        using (var response = getCurrentTenantRequest.GetResponse())
        {
            tenantUrl = response.Headers["Location"];
        }
    }
    catch (WebException wex)
    {
        Console.WriteLine("Request failed. {0}", wex.Message);
        throw;
    }
    // Now that we have the real tenant URL, get the tenant info
    string tenantData = null;
    var getTenantInfoRequest = BuildRequest("GET", tenantUrl);
    try
    {
        using (var response = getTenantInfoRequest.GetResponse())
        using (var responseStream = response.GetResponseStream())
        using (var reader = new StreamReader(responseStream))
        {
            tenantData = reader.ReadToEnd();
        }
    }
    catch (WebException wex)
    {
        Console.WriteLine("Request failed. {0}", wex.Message);
        throw;
    }
    // Use JSON.NET to parse the data and get the tenant name
    var parsedData = JsonConvert.DeserializeObject<Dictionary<string, object>>(tenantData);
    Console.WriteLine("Current tenant is: {0}", parsedData["name"]);
    // Wait for user input
    Console.ReadKey(false);
}

代码非常冗长,因为我们正在向API发出原始请求。同样,如果您经常发出请求,请使用SDK!