17

我正在使用声明身份验证使用 ASP.NET WebApi 2 构建 Web api,我的用户可以拥有大量声明。随着大量索赔,不记名令牌会迅速增长,因此我试图找到一种返回更短的不记名令牌的方法。

到目前为止,我发现我可以为IAuthenticationTokenProviderOAuth 选项OAuthAuthorizationServerOptions.AccessTokenProvider属性提供一个:

OAuthOptions = new OAuthAuthorizationServerOptions
{
    TokenEndpointPath = new PathString("/Token"),
    Provider = new ApplicationOAuthProvider(PublicClientId),
    AccessTokenExpireTimeSpan = TimeSpan.FromHours(12),
    AccessTokenProvider = new GuidProvider() // <-- here
};

这让我有机会拦截AuthenticationTicket并把它藏起来,用更简单的东西代替它——在我下面的散列 guid 示例中。(注意:目前这个类只是在ConcurrentDictionary<string,AuthenticationTicket>我的会话中保存 - 在一个真实的示例中,我打算将会话存储在一些持久性存储中)

public class GuidProvider : IAuthenticationTokenProvider
{
    private static ConcurrentDictionary<string, AuthenticationTicket> tokens 
        = new ConcurrentDictionary<string, AuthenticationTicket>();

    public void Create(AuthenticationTokenCreateContext context)
    {
        throw new NotImplementedException();
    }

    public async System.Threading.Tasks.Task CreateAsync(AuthenticationTokenCreateContext context)
    {
        var guid = Guid.NewGuid().ToString();

        var ticket = Crypto.Hash(guid);

        tokens.TryAdd(ticket, context.Ticket);

        context.SetToken(ticket);
    }

    public void Receive(AuthenticationTokenReceiveContext context)
    {
        throw new NotImplementedException();
    }

    public async System.Threading.Tasks.Task ReceiveAsync(AuthenticationTokenReceiveContext context)
    {
        AuthenticationTicket ticket;

        if (tokens.TryGetValue(context.Token, out ticket))
        {
            if (ticket.Properties.ExpiresUtc.Value < DateTime.UtcNow)
            {
                tokens.TryRemove(context.Token, out ticket);
            }
            context.SetTicket(ticket);
        }
    }
}

所以我的问题:

  • 这是提供代理密钥来代替我的长声明生成令牌的适当(且安全!)方式吗?
  • 在 webapi/OAuth 堆栈中是否有更好/更容易的地方我应该这样做?

另一件需要注意的事情是我打算支持刷新令牌,实际上上面的示例是从使用这种机制的示例中提取的刷新令牌 - 除了刷新令牌它们似乎是一次性使用的,所以该 ReceiveAsync方法通常总是会删除从提供的刷新令牌ConcurrentDictionary,我不完全确定我理解为什么?

4

1 回答 1

7

我不建议这样做,因为您最终会将身份验证票证存储到数据库或 Redis 服务器中,这里的缺点是,对于每个包含不记名令牌的请求,您将检查这个永久存储以解决指导并再次获得票以构建它。

我建议您使用 JSON Web Token JWT而不是默认的不记名访问令牌格式,为此您需要CustomOAuthProvider在属性Provider中实现自定义访问令牌格式,OAuthAuthorizationServerOptions如下代码所示:

 OAuthAuthorizationServerOptions OAuthServerOptions = new OAuthAuthorizationServerOptions()
        {
            //For Dev enviroment only (on production should be AllowInsecureHttp = false)
            AllowInsecureHttp = true,
            TokenEndpointPath = new PathString("/oauth2/token"),
            AccessTokenExpireTimeSpan = TimeSpan.FromMinutes(30),
            Provider = new CustomOAuthProvider(),
            AccessTokenFormat = new CustomJwtFormat("http://jwtauthzsrv.azurewebsites.net")
        };

我注意到,向 JWT 令牌添加更多声明不会像默认访问令牌格式那样显着增加其大小。

在一个包含不同声明的 2 个 JWT 样本下方,第二个比第一个仅大 50 个字符。我建议您使用jwt.io First JWT检查每个编码的内容:

eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJ1bmlxdWVfbmFtZSI6InRhaXNlZXIiLCJzdWIiOiJ0YWlzZWVyIiwicm9sZSI6WyJNYW5hZ2VyIiwiU3VwZXJ2aXNvciJdLCJpc3MiOiJodHRwOi8vand0YXV0aHpzcnYuYXp1cmV3ZWJzaXRlcy5uZXQiLCJhdWQiOiIwOTkxNTNjMjYyNTE0OWJjOGVjYjNlODVlMDNmMDAyMiIsImV4cCI6MTQxODY0NzMyNywibmJmIjoxNDE4NjQ1NTI3fQ.vH9XPtjtAv2-6SwlyX4fKNJfm5ZTVHd_9a3bRgkA_LI

第二个 JWT(更多声明):

eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJ1bmlxdWVfbmFtZSI6InRhaXNlZXIiLCJzdWIiOiJ0YWlzZWVyIiwicm9sZSI6WyJNYW5hZ2VyIiwiU3VwZXJ2aXNvciIsIlN1cGVydmlzb3IxIiwiU3VwZXJ2aXNvcjIiLCJTdXBlcnZpc29yMyJdLCJpc3MiOiJodHRwOi8vand0YXV0aHpzcnYuYXp1cmV3ZWJzaXRlcy5uZXQiLCJhdWQiOiIwOTkxNTNjMjYyNTE0OWJjOGVjYjNlODVlMDNmMDAyMiIsImV4cCI6MTQxODY0NzQ1NiwibmJmIjoxNDE4NjQ1NjU2fQ.TFEGDtz1RN8VmCQu7JH4Iug0B8UlWDLVrIlvc-7IK3E

JWT 格式正在成为发布 OAuth 2.0 不记名令牌的标准方式,它也将与刷新令牌授权一起使用。但请记住,JWT 只是签名令牌,而不是默认访问令牌格式的加密,所以不要存储机密数据。

我在 bitoftech.net 上写了详细的博客文章,介绍了如何在 ASP.NET Web API 中使用 JWT 令牌以及 GIthub 上的实时演示 API 和源代码,请随时查看,如果您需要更多帮助,请告诉我。

祝你好运!

于 2014-12-15T12:33:03.277 回答