为每种客户端应用程序发布应用程序 ID
Issue Application Id for each kind of Client Application
我正在使用 Microsoft Owin
和 ASP.NET WebApi
进行客户端应用程序的身份验证和授权过程。此外,身份验证服务器由 HTTPS
保护。我已经阅读了一些关于使用 Microsoft Owin
的文章,我选择实施的其中一篇是:
Token Based Authentication using ASP.NET Web API 2, Owin, and Identity
我的项目和那个实现之间存在一些差异:
如果请求是由我的应用程序在移动设备上发送的,我需要识别我的客户端 phone,而不是任何其他设备或工具,如 Fiddler。我认为其中一个选项可能是通过来自移动应用程序的每个请求发送一个应用程序 ID。但是我不知道应该如何以及在哪里验证身份验证服务器应用程序中的请求。这对于注册用户非常重要:
[AllowAnonymous]
[Route("Register")]
public async Task<IHttpActionResult> Register(UserModel userModel)
{
if (!ModelState.IsValid)
{
return BadRequest(ModelState);
}
IdentityResult result = await _repo.RegisterUser(userModel);
IHttpActionResult errorResult = GetErrorResult(result);
if (errorResult != null)
{
return errorResult;
}
return Ok();
}
我不想让不可靠的设备,即除移动应用程序之外的客户端调用此方法。
- 我需要让匿名用户从网站购买一些产品,但我不知道在不进行身份验证的情况下为匿名用户颁发令牌的最佳做法是什么。
如果你想识别你的客户端并授权它,你可以覆盖方法ValidateClientAuthentication
。
在您链接的 Taiseer 示例中,您会找到一些代码:
public override async Task ValidateClientAuthentication(OAuthValidateClientAuthenticationContext context)
{
context.Validated();
}
和一张便条,上面写着:
As you notice this class inherits from class
“OAuthAuthorizationServerProvider”, we’ve overridden two methods
“ValidateClientAuthentication” and “GrantResourceOwnerCredentials”.
The first method is responsible for validating the “Client”, in our
case we have only one client so we’ll always return that its validated
successfully.
如果你想验证客户端,你必须在其中添加一些逻辑。
通常,您会在 http 请求的 header 中传递一个 clientId
和一个 clientSecret
,这样您就可以使用一些数据库参数来验证客户端的请求,例如
public override async Task ValidateClientAuthentication(OAuthValidateClientAuthenticationContext context)
{
string clientId = string.Empty;
string clientSecret = string.Empty;
if (!context.TryGetBasicCredentials(out clientId, out clientSecret))
{
context.TryGetFormCredentials(out clientId, out clientSecret);
}
if (context.ClientId == null)
{
context.SetError("invalid_client", "Client credentials could not be retrieved through the Authorization header.");
context.Rejected();
return;
}
try
{
// You're going to check the client's credentials on a database.
if (clientId == "MyApp" && clientSecret == "MySecret")
{
context.Validated(clientId);
}
else
{
// Client could not be validated.
context.SetError("invalid_client", "Client credentials are invalid.");
context.Rejected();
}
}
catch (Exception ex)
{
string errorMessage = ex.Message;
context.SetError("server_error");
context.Rejected();
}
return;
}
在上面的示例中,您将尝试提取在请求的 header 中发送的客户端凭据:
if (!context.TryGetBasicCredentials(out clientId, out clientSecret))
{
context.TryGetFormCredentials(out clientId, out clientSecret);
}
并验证了它们:
// You're going to check the client's credentials on a database.
if (clientId == "MyApp" && clientSecret == "MySecret")
{
context.Validated(clientId);
}
如果客户端发送了错误的请求header您需要拒绝该请求:
context.SetError("invalid_client", "Client credentials are invalid.");
context.Rejected();
方法 ValidateClientAuthentication
在 GrantResourceOwnerCredentials
之前处理。通过这种方式,您可以扩展它并向 GrantResourceOwnerCredentials 传递一些您可能需要的额外信息。
在我的一个应用程序中,我创建了一个 class:
class ApplicationClient
{
public string Id { get; set; }
public string Name { get; set; }
public string ClientSecretHash { get; set; }
public OAuthGrant AllowedGrant { get; set; }
public DateTimeOffset CreatedOn { get; set; }
}
我在检查 clientId 和密码后立即在 ValidateClientAuthentication 中使用它:
if (clientId == "MyApp" && clientSecret == "MySecret")
{
ApplicationClient client = new ApplicationClient();
client.Id = clientId;
client.AllowedGrant = OAuthGrant.ResourceOwner;
client.ClientSecretHash = new PasswordHasher().HashPassword("MySecret");
client.Name = "My App";
client.CreatedOn = DateTimeOffset.UtcNow;
context.OwinContext.Set<ApplicationClient>("oauth:client", client);
context.Validated(clientId);
}
如您所见
context.OwinContext.Set<ApplicationClient>("oauth:client", client);
我正在设置一个 Owin 变量,我可以稍后阅读。在您的 GrantResourceOwnerCredentials
中,您现在可以读取该变量以备不时之需:
public override async Task GrantResourceOwnerCredentials(OAuthGrantResourceOwnerCredentialsContext context)
{
ApplicationClient client = context.OwinContext.Get<ApplicationClient>("oauth:client");
...
}
现在,如果您想获取不记名令牌 - 您将用于所有安全 API 调用 - 您需要对 clientId
和 clientSecret
进行编码(base64) 并将其传递到请求的 header 中:
带有 jquery 的 ajax 请求看起来像这样:
var clientId = "MyApp";
var clientSecret = "MySecret";
var authorizationBasic = $.base64.btoa(clientId + ':' + clientSecret);
$.ajax({
type: 'POST',
url: '<your API token validator>',
data: { username: 'John', password: 'Smith', grant_type: 'password' },
dataType: "json",
contentType: 'application/x-www-form-urlencoded; charset=utf-8',
xhrFields: {
withCredentials: true
},
headers: {
'Authorization': 'Basic ' + authorizationBasic
},
beforeSend: function (xhr) {
},
success: function (result) {
var token = result.access_token;
},
error: function (req, status, error) {
alert(error);
}
});
如您所见,我还在请求的 body 中添加了用户名和密码 - 以及授权类型:
data: { username: 'John', password: 'Smith', grant_type: 'password' }
以便服务器能够验证客户端(clientId + clientSecret)和用户(用户名+密码)。
如果请求成功,您应该取回一个有效令牌:
oAuth.Token = result.access_token;
您可以将其存储在某处以用于以下请求。
现在您可以将此令牌用于对 api:
的所有请求
$.ajax({
type: 'GET',
url: 'myapi/fetchCustomer/001',
data: { },
dataType: "json",
headers: {
'Authorization': 'Bearer ' + oAuth.Token
},
success: function (result) {
// your customer is in the result.
},
error: function (req, status, error) {
alert(error);
}
});
在启动期间您可能想要添加到 API 的另一件事是 SuppressDefaultHostAuthentication
:
config.SuppressDefaultHostAuthentication();
这是HttpConfiguration
的扩展方法。由于您使用的是不记名令牌,因此您希望禁止使用标准 cookie-based 身份验证机制。
Taiseer 写了另一个系列articles,值得一读,他解释了所有这些事情。
我已经创建了一个 github repo 在那里你可以看到它是如何工作的。
Web API 是 self-hosted 并且有两个客户端:jQuery 和控制台应用程序。
我正在使用 Microsoft Owin
和 ASP.NET WebApi
进行客户端应用程序的身份验证和授权过程。此外,身份验证服务器由 HTTPS
保护。我已经阅读了一些关于使用 Microsoft Owin
的文章,我选择实施的其中一篇是:
Token Based Authentication using ASP.NET Web API 2, Owin, and Identity
我的项目和那个实现之间存在一些差异:
如果请求是由我的应用程序在移动设备上发送的,我需要识别我的客户端 phone,而不是任何其他设备或工具,如 Fiddler。我认为其中一个选项可能是通过来自移动应用程序的每个请求发送一个应用程序 ID。但是我不知道应该如何以及在哪里验证身份验证服务器应用程序中的请求。这对于注册用户非常重要:
[AllowAnonymous] [Route("Register")] public async Task<IHttpActionResult> Register(UserModel userModel) { if (!ModelState.IsValid) { return BadRequest(ModelState); } IdentityResult result = await _repo.RegisterUser(userModel); IHttpActionResult errorResult = GetErrorResult(result); if (errorResult != null) { return errorResult; } return Ok(); }
我不想让不可靠的设备,即除移动应用程序之外的客户端调用此方法。
- 我需要让匿名用户从网站购买一些产品,但我不知道在不进行身份验证的情况下为匿名用户颁发令牌的最佳做法是什么。
如果你想识别你的客户端并授权它,你可以覆盖方法ValidateClientAuthentication
。
在您链接的 Taiseer 示例中,您会找到一些代码:
public override async Task ValidateClientAuthentication(OAuthValidateClientAuthenticationContext context)
{
context.Validated();
}
和一张便条,上面写着:
As you notice this class inherits from class “OAuthAuthorizationServerProvider”, we’ve overridden two methods “ValidateClientAuthentication” and “GrantResourceOwnerCredentials”. The first method is responsible for validating the “Client”, in our case we have only one client so we’ll always return that its validated successfully.
如果你想验证客户端,你必须在其中添加一些逻辑。
通常,您会在 http 请求的 header 中传递一个 clientId
和一个 clientSecret
,这样您就可以使用一些数据库参数来验证客户端的请求,例如
public override async Task ValidateClientAuthentication(OAuthValidateClientAuthenticationContext context)
{
string clientId = string.Empty;
string clientSecret = string.Empty;
if (!context.TryGetBasicCredentials(out clientId, out clientSecret))
{
context.TryGetFormCredentials(out clientId, out clientSecret);
}
if (context.ClientId == null)
{
context.SetError("invalid_client", "Client credentials could not be retrieved through the Authorization header.");
context.Rejected();
return;
}
try
{
// You're going to check the client's credentials on a database.
if (clientId == "MyApp" && clientSecret == "MySecret")
{
context.Validated(clientId);
}
else
{
// Client could not be validated.
context.SetError("invalid_client", "Client credentials are invalid.");
context.Rejected();
}
}
catch (Exception ex)
{
string errorMessage = ex.Message;
context.SetError("server_error");
context.Rejected();
}
return;
}
在上面的示例中,您将尝试提取在请求的 header 中发送的客户端凭据:
if (!context.TryGetBasicCredentials(out clientId, out clientSecret))
{
context.TryGetFormCredentials(out clientId, out clientSecret);
}
并验证了它们:
// You're going to check the client's credentials on a database.
if (clientId == "MyApp" && clientSecret == "MySecret")
{
context.Validated(clientId);
}
如果客户端发送了错误的请求header您需要拒绝该请求:
context.SetError("invalid_client", "Client credentials are invalid.");
context.Rejected();
方法 ValidateClientAuthentication
在 GrantResourceOwnerCredentials
之前处理。通过这种方式,您可以扩展它并向 GrantResourceOwnerCredentials 传递一些您可能需要的额外信息。
在我的一个应用程序中,我创建了一个 class:
class ApplicationClient
{
public string Id { get; set; }
public string Name { get; set; }
public string ClientSecretHash { get; set; }
public OAuthGrant AllowedGrant { get; set; }
public DateTimeOffset CreatedOn { get; set; }
}
我在检查 clientId 和密码后立即在 ValidateClientAuthentication 中使用它:
if (clientId == "MyApp" && clientSecret == "MySecret")
{
ApplicationClient client = new ApplicationClient();
client.Id = clientId;
client.AllowedGrant = OAuthGrant.ResourceOwner;
client.ClientSecretHash = new PasswordHasher().HashPassword("MySecret");
client.Name = "My App";
client.CreatedOn = DateTimeOffset.UtcNow;
context.OwinContext.Set<ApplicationClient>("oauth:client", client);
context.Validated(clientId);
}
如您所见
context.OwinContext.Set<ApplicationClient>("oauth:client", client);
我正在设置一个 Owin 变量,我可以稍后阅读。在您的 GrantResourceOwnerCredentials
中,您现在可以读取该变量以备不时之需:
public override async Task GrantResourceOwnerCredentials(OAuthGrantResourceOwnerCredentialsContext context)
{
ApplicationClient client = context.OwinContext.Get<ApplicationClient>("oauth:client");
...
}
现在,如果您想获取不记名令牌 - 您将用于所有安全 API 调用 - 您需要对 clientId
和 clientSecret
进行编码(base64) 并将其传递到请求的 header 中:
带有 jquery 的 ajax 请求看起来像这样:
var clientId = "MyApp";
var clientSecret = "MySecret";
var authorizationBasic = $.base64.btoa(clientId + ':' + clientSecret);
$.ajax({
type: 'POST',
url: '<your API token validator>',
data: { username: 'John', password: 'Smith', grant_type: 'password' },
dataType: "json",
contentType: 'application/x-www-form-urlencoded; charset=utf-8',
xhrFields: {
withCredentials: true
},
headers: {
'Authorization': 'Basic ' + authorizationBasic
},
beforeSend: function (xhr) {
},
success: function (result) {
var token = result.access_token;
},
error: function (req, status, error) {
alert(error);
}
});
如您所见,我还在请求的 body 中添加了用户名和密码 - 以及授权类型:
data: { username: 'John', password: 'Smith', grant_type: 'password' }
以便服务器能够验证客户端(clientId + clientSecret)和用户(用户名+密码)。
如果请求成功,您应该取回一个有效令牌:
oAuth.Token = result.access_token;
您可以将其存储在某处以用于以下请求。
现在您可以将此令牌用于对 api:
的所有请求$.ajax({
type: 'GET',
url: 'myapi/fetchCustomer/001',
data: { },
dataType: "json",
headers: {
'Authorization': 'Bearer ' + oAuth.Token
},
success: function (result) {
// your customer is in the result.
},
error: function (req, status, error) {
alert(error);
}
});
在启动期间您可能想要添加到 API 的另一件事是 SuppressDefaultHostAuthentication
:
config.SuppressDefaultHostAuthentication();
这是HttpConfiguration
的扩展方法。由于您使用的是不记名令牌,因此您希望禁止使用标准 cookie-based 身份验证机制。
Taiseer 写了另一个系列articles,值得一读,他解释了所有这些事情。
我已经创建了一个 github repo 在那里你可以看到它是如何工作的。
Web API 是 self-hosted 并且有两个客户端:jQuery 和控制台应用程序。