为每种客户端应用程序发出应用程序ID

Sam*_*ami 10 authentication asp.net-web-api owin

我使用Microsoft OwinASP.NET WebApi进行身份验证和授权过程对我的客户端应用程序.身份验证服务器也受到保护HTTPS.我已经阅读了一些关于使用的文章Microsoft Owin,其中一篇我选择实现的是: 使用ASP.NET Web API 2,Owin和Identity进行基于令牌的身份验证

我的项目与实施之间存在一些差异:

  1. 我需要识别我的客户端,以防我的应用程序在手机上发送请求,而不是任何其他设备或工具,如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();
        }
    
    Run Code Online (Sandbox Code Playgroud)

    我不想让不可靠的设备(即除移动应用程序之外的客户端)调用此方法.

  2. 我需要让匿名用户从网站上购买一些产品,但我不知道在没有进行身份验证的情况下为匿名用户发放令牌的最佳做法是什么.

Lef*_*tyX 6

如果要识别客户端并对其进行授权,可以覆盖该方法ValidateClientAuthentication.

在Taiseer的示例中,您已找到一些代码:

public override async Task ValidateClientAuthentication(OAuthValidateClientAuthenticationContext context)
{
    context.Validated();
}
Run Code Online (Sandbox Code Playgroud)

并附上一条说明:

当你注意到这个类继承自"OAuthAuthorizationServerProvider"类时,我们已经覆盖了两个方法"ValidateClientAuthentication"和"GrantResourceOwnerCredentials".第一种方法负责验证"客户端",在我们的例子中,我们只有一个客户端,因此我们将始终返回成功验证的客户端.

如果你想验证客户端,你必须在那里放置一些逻辑.
通常,您可以在http请求的标头中传递a clientId和a 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;
}
Run Code Online (Sandbox Code Playgroud)

在上面的示例中,您将尝试提取在请求标头中发送的客户端凭据:

if (!context.TryGetBasicCredentials(out clientId, out clientSecret))
{
    context.TryGetFormCredentials(out clientId, out clientSecret);
}
Run Code Online (Sandbox Code Playgroud)

并验证它们:

// You're going to check the client's credentials on a database.
if (clientId == "MyApp" && clientSecret == "MySecret")
{
    context.Validated(clientId);
}
Run Code Online (Sandbox Code Playgroud)

如果客户端发送错误的请求标头,则需要拒绝该请求:

context.SetError("invalid_client", "Client credentials are invalid.");
context.Rejected();
Run Code Online (Sandbox Code Playgroud)

ValidateClientAuthentication之前处理该方法GrantResourceOwnerCredentials.这样您就可以扩展它并将GrantResourceOwnerCredentials传递给您可能需要的一些额外信息.

在我的一个应用程序中,我创建了一个类:

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; }
}
Run Code Online (Sandbox Code Playgroud)

我在检查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);
}
Run Code Online (Sandbox Code Playgroud)

正如你在这里看到的那样

context.OwinContext.Set<ApplicationClient>("oauth:client", client);
Run Code Online (Sandbox Code Playgroud)

我正在设置一个Owin变量,我稍后可以阅读.在您GrantResourceOwnerCredentials现在,您可以在需要时阅读该变量:

public override async Task GrantResourceOwnerCredentials(OAuthGrantResourceOwnerCredentialsContext context)
{
    ApplicationClient client = context.OwinContext.Get<ApplicationClient>("oauth:client");
    ...
}
Run Code Online (Sandbox Code Playgroud)

现在,如果要获取承载令牌 - 您将用于所有安全API调用 - 您需要对您的clientIdclientSecret(base64)进行编码并将其传递到请求的标头中:

使用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);
        }
});
Run Code Online (Sandbox Code Playgroud)

正如您所看到的,我还在请求正文中添加了用户名和密码(使用授权类型):

data: { username: 'John', password: 'Smith', grant_type: 'password' }
Run Code Online (Sandbox Code Playgroud)

这样服务器就能验证客户端(clientId + clientSecret)和用户(用户名+密码).

如果请求成功,您应该获得有效的令牌:

oAuth.Token = result.access_token;
Run Code Online (Sandbox Code Playgroud)

您可以在某处存储以满足以下请求.

现在,您可以将此令牌用于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);
    }
});
Run Code Online (Sandbox Code Playgroud)

您可能希望在启动期间添加到API的另一件事是SuppressDefaultHostAuthentication:

config.SuppressDefaultHostAuthentication();
Run Code Online (Sandbox Code Playgroud)

这是一种扩展方法HttpConfiguration.由于您正在使用承载令牌,因此您希望禁止基于cookie的标准身份验证机制.

Taiseer撰写了另一系列文章,值得一读,他解释了所有这些内容.

我创建了一个github仓库,你可以看到它是如何工作的.
Web API是自托管的,有两个客户端:jQuery和Console Application.