401 使用 WebRequest 对象调用 Stormpath REST API 时未经授权?
401 Unauthorized when calling Stormpath REST API with WebRequest object?
我正在使用 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()
我遇到了一个例外:
An unhandled exception of type 'System.Net.WebException' occurred in System.dll The remote server returned an error: (401) Unauthorized.
你能帮我看看我的代码有没有问题?
更新-使用SDK,更简单!
如果您经常从 C# 调用 Stormpath API,请不要费心手动编写请求。请改用 Stormpath .NET SDK。我是作者。 :)
使用 install-package Stormpath.SDK
从程序包管理器控制台安装它。然后,创建一个 IClient
object:
// 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}");
如果你真的想发出原始请求,你仍然可以这样做!我会在下面解释。
构建授权header
401 Unauthorized
响应意味着 API 无法在您的请求中找到有效的授权 header。要正确验证,您需要两件事:
- 格式为
apiKeyID:apiKeySecret
的授权负载
- 一个
Authorization
header 的值为:Basic base64(payload)
下面是如何构造完整的 header:
// 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
东西。使用上述 header,请求将被 API 视为有效请求(当然,假设您的 API Key ID 和 Secret 是正确的)。
重定向处理
需要注意的一件事(一开始这让我感到困惑!)是 WebRequest 将自动遵循 HTTP 302 重定向,但 will not apply the existing headers 到新请求。
解决方案是禁用以下重定向:
request.AllowAutoRedirect = false;
这意味着您必须自己处理 302 响应,但这是将授权 header 正确应用到每个请求的唯一方法。
工作示例
我在 this gist 中创建了一个简单的工作示例。因为我会多次创建请求,所以我写了一个辅助函数:
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!
我正在使用 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()
我遇到了一个例外:
An unhandled exception of type 'System.Net.WebException' occurred in System.dll The remote server returned an error: (401) Unauthorized.
你能帮我看看我的代码有没有问题?
更新-使用SDK,更简单!
如果您经常从 C# 调用 Stormpath API,请不要费心手动编写请求。请改用 Stormpath .NET SDK。我是作者。 :)
使用 install-package Stormpath.SDK
从程序包管理器控制台安装它。然后,创建一个 IClient
object:
// 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}");
如果你真的想发出原始请求,你仍然可以这样做!我会在下面解释。
构建授权header
401 Unauthorized
响应意味着 API 无法在您的请求中找到有效的授权 header。要正确验证,您需要两件事:
- 格式为
apiKeyID:apiKeySecret
的授权负载
- 一个
Authorization
header 的值为:Basic base64(payload)
下面是如何构造完整的 header:
// 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
东西。使用上述 header,请求将被 API 视为有效请求(当然,假设您的 API Key ID 和 Secret 是正确的)。
重定向处理
需要注意的一件事(一开始这让我感到困惑!)是 WebRequest 将自动遵循 HTTP 302 重定向,但 will not apply the existing headers 到新请求。
解决方案是禁用以下重定向:
request.AllowAutoRedirect = false;
这意味着您必须自己处理 302 响应,但这是将授权 header 正确应用到每个请求的唯一方法。
工作示例
我在 this gist 中创建了一个简单的工作示例。因为我会多次创建请求,所以我写了一个辅助函数:
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!