为每种客户端应用程序

时间:2015-09-05 18:14:01

标签: authentication asp.net-web-api owin

我正在使用Microsoft OwinASP.NET WebApi进行客户端应用程序的身份验证和授权过程。身份验证服务器也由HTTPS保护。我已经阅读了一些关于使用Microsoft Owin的文章,其中一个我选择实现的是: Token Based Authentication using ASP.NET Web API 2, Owin, and 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();
        }
    

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

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

1 个答案:

答案 0 :(得分:6)

如果您想识别您的客户并对其进行授权,您可以覆盖方法ValidateClientAuthentication

在Taiseer的例子中,你已经找到了一些代码:

public override async Task ValidateClientAuthentication(OAuthValidateClientAuthenticationContext context)
{
    context.Validated();
}

并注明:

  

正如您所注意到的,此类继承自类   “OAuthAuthorizationServerProvider”,我们已经覆盖了两个方法   “ValidateClientAuthentication”和“GrantResourceOwnerCredentials”。   第一种方法负责验证我们的“客户”   我们只有一个客户端,所以我们总是返回它的验证   成功。

如果你想验证客户端,你必须在那里放置一些逻辑 通常,您会在http请求的标头中传递clientIdclientSecret,以便您可以使用某些数据库参数验证客户端的请求。

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

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

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

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

context.SetError("invalid_client", "Client credentials are invalid.");
context.Rejected();

方法ValidateClientAuthenticationGrantResourceOwnerCredentials之前处理。这样您就可以扩展它并将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; }
}

我在检查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调用),则需要对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);
        }
});

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

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的标准身份验证机制。

Taiseer写了另一系列articles,值得一读,他解释了所有这些事情。

我创建了github repo,您可以在其中查看其工作原理 Web API是自托管的,有两个客户端:jQuery和Console Application。