使用OAuth Bearer Tokens Generation和Owin在WebApi中向客户端返回更多信息

64

我已经创建了一个WebApi和一个Cordova应用程序。 我正在使用HTTP请求在Cordova应用程序和WebAPI之间进行通信。 在WebAPI中,我已经实现了OAuth Bearer令牌生成。

public void ConfigureOAuth(IAppBuilder app)
    {
        var oAuthServerOptions = new OAuthAuthorizationServerOptions
        {
            AllowInsecureHttp = true,
            TokenEndpointPath = new PathString("/token"),
            AccessTokenExpireTimeSpan = TimeSpan.FromDays(1),
            Provider = new SimpleAuthorizationServerProvider(new UserService(new Repository<User>(new RabbitApiObjectContext()), new EncryptionService()))
        };

        // Token Generation
        app.UseOAuthAuthorizationServer(oAuthServerOptions);
        app.UseOAuthBearerAuthentication(new OAuthBearerAuthenticationOptions());

    }

这是在SimpleAuthorizationServerProvider实现内部

 public override async Task GrantResourceOwnerCredentials(OAuthGrantResourceOwnerCredentialsContext context)
    {
       context.OwinContext.Response.Headers.Add("Access-Control-Allow-Origin", new[] { "*" });

        // A little hack. context.UserName contains the email
        var user = await _userService.GetUserByEmailAndPassword(context.UserName, context.Password);

        if (user == null)
        {
            context.SetError("invalid_grant", "Wrong email or password.");
            return;
        }

        var identity = new ClaimsIdentity(context.Options.AuthenticationType);
        identity.AddClaim(new Claim("sub", context.UserName));
        identity.AddClaim(new Claim("role", "user"));

        context.Validated(identity);
    }

从Cordova应用程序成功登录API后,我收到以下JSON:

{"access_token":"some token","token_type":"bearer","expires_in":86399}
问题是,我需要更多关于用户的信息。例如,数据库中有一个UserGuid字段,当登录成功时,我想将其发送给Cordova应用程序,并在以后的请求中使用它。除了"access_token"、"token_type"和"expires_in"之外,我能包含其他信息返回给客户端吗?如果不能,那么我如何根据access_token在API中获取用户?
编辑:

我认为我找到了一种解决方法。我在GrantResourceOwnerCredentials中添加了以下代码:

identity.AddClaim(new Claim(ClaimTypes.Name, user.UserGuid.ToString()));

之后,在我的控制器中,我可以这样访问GUID:User.Identity.Name

我还可以添加带有自定义名称的guid identity.AddClaim(new Claim("guid", user.UserGuid.ToString()));

我仍然想知道是否有一种方法可以通过Bearer Token JSON向客户端返回更多数据。

3个回答

98
您可以添加任意数量的声明。
您可以添加来自 System.Security.Claims 的标准声明,或创建自己的声明。
声明将在令牌中加密,因此它们仅能从资源服务器上访问。
如果您希望客户端读取令牌的扩展属性,则可以选择另一种选项:AuthenticationProperties
假设您想要添加某些内容以供客户端访问。那么就这么做:
var props = new AuthenticationProperties(new Dictionary<string, string>
{
    { 
        "surname", "Smith"
    },
    { 
        "age", "20"
    },
    { 
    "gender", "Male"
    }
});

现在,您可以使用上面添加的属性创建一张工单:
var ticket = new AuthenticationTicket(identity, props);
context.Validated(ticket);

这是您的客户将获取的结果:

.expires: "Tue, 14 Oct 2014 20:42:52 GMT"
.issued: "Tue, 14 Oct 2014 20:12:52 GMT"
access_token: "blahblahblah"
expires_in: 1799
age: "20"
gender: "Male"
surname: "Smith"
token_type: "bearer"

另一方面,如果您添加声明,您将能够在资源服务器和API控制器中读取它们:

public IHttpActionResult Get()
{
    ClaimsPrincipal principal = Request.GetRequestContext().Principal as ClaimsPrincipal;

    return Ok();
}

您的ClaimsPrincipal将包含您在此处添加的新声明的guid

identity.AddClaim(new Claim("guid", user.UserGuid.ToString()));

如果你想了解更多有关OWIN,Bearer令牌和Web API的内容,这里有一篇非常好的教程here,而这篇文章则可以帮助你掌握授权服务器和资源服务器背后的所有概念。

更新:

你可以在这里找到一个可工作的示例。这是一个自托管的Web Api+Owin。 这里没有涉及数据库。客户端是一个控制台应用程序(也有html + JavaScript示例),它调用一个Web Api来传递凭据。

如Taiseer建议的那样,你需要覆盖TokenEndpoint

public override Task TokenEndpoint(OAuthTokenEndpointContext context)
{
    foreach (KeyValuePair<string, string> property in context.Properties.Dictionary)
    {
        context.AdditionalResponseParameters.Add(property.Key, property.Value);
    }

    return Task.FromResult<object>(null);
}

从“解决方案 -> 属性”启用“多个启动项目”,您可以立即运行它。


2
谢谢回复。我编写API时读了完全相同的文章,甚至下载了代码并仔细阅读。因此,我尝试使用“AuthenticationProperties”,但结果仍然是一样的。在客户端上,我仍然只收到“access_token”、“token_type”和“expires_in”属性。 - Ivan Stoyanov
3
除非你覆盖 Taiseer 的回答中的 public override Task TokenEndpoint(OAuthTokenEndpointContext context) 方法,否则这将不起作用。 - Ers
@Ers:是的,你说得对。忘记提到了...但它在我的仓库里。感谢你的投票。 - LeftyX
由于某些原因,我的JSON返回值不包括".expires"和".issued"属性。是否需要进行其他配置?谢谢。 - nav
@nav:很难说。它应该可以工作。你有机会在我的Github存储库中运行代码吗?干杯。 - LeftyX
显示剩余6条评论

45

如果不需要,我建议不要向令牌添加额外的声明,因为这会增加令牌的大小,并且您将在每个请求中继续发送它。

如LeftyX所建议,将它们添加为属性,但请确保覆盖TokenEndPoint方法,在成功获取令牌时将这些属性作为响应获取,如果没有此端点,则属性将不会在响应中返回。

 public override Task TokenEndpoint(OAuthTokenEndpointContext context)
    {
        foreach (KeyValuePair<string, string> property in context.Properties.Dictionary)
        {
            context.AdditionalResponseParameters.Add(property.Key, property.Value);
        }

        return Task.FromResult<object>(null);
    }

你可以在这里检查我的repo,获得完整示例。希望它能有所帮助。


@TaiseerJoudeh 你知道我是否可以将字符串数组作为属性之一吗?我需要发送一个权限列表。 - pomber
截至今天,在Asp.Net Core中,使用ASOS似乎有了一些变化。context.Properties.Dictionary已经变成了context.Ticket.Properties.Items,但我找不到context.AdditionalResponseParameters的等效项。有人知道吗? - superjos
没事了,ASOS的作者把我链接到了这里 - superjos
@TaiseerJoudeh,是否可以包括其他用户模型属性,例如user.birthday?我在上下文中看到的唯一用户属性是context.username。如何包括其他用户属性? - Jose Miguel Vega Lopez
1
@AminM 如果您想要身份验证,可以手动添加它们作为声明。AddClaim(new Claim("Family", "Family name value")); - Taiseer Joudeh
显示剩余2条评论

0
GrantResourceOwnerCredentials 函数中,您可以使用以下行将更多信息添加到响应中。
ticket.Properties.Dictionary.Add(new KeyValuePair<string, string>("Tag", "Data")); // Or whatever data you want to add

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接