ASP.NET Core的Cookie身份验证

9

我可以在ITicketStore中使用MemoryCache来存储AuthenticationTicket吗?

背景:我的Web应用程序正在使用Cookie身份验证:

app.UseCookieAuthentication(new CookieAuthenticationOptions
{
    AutomaticAuthenticate = true,
    AutomaticChallenge = true,
    LoginPath = new PathString("/Authentication/SignIn"),
    LogoutPath = new PathString("/Authentication/SignOut"),
    ReturnUrlParameter = "/Authentication/SignIn"
});

我的 Web API 使用访问令牌(OAuth2)来处理授权过程。
有时候(在某些浏览器上),会抛出以下异常:
未经处理的异常:分块 cookie 不完整。只找到了预期的 2 个块中的 1 个,总共 4021 个字符。可能已经超过了客户端大小限制。
显然,cookie 太大了。这很奇怪,因为我没有使用很多声明。它们都是默认声明(nameidentifier、nonce、exp 等)。现在,我正在尝试将自己的 ITicketStore 实现为 CookieAuthenticationOptions 上的 SessionStore。AuthenticationTicket 将存储在 MemoryCache 中(就像在这个 示例 中一样)。我对这整个主题非常陌生,不确定这是否是一个好方法,以及 MemoryCache 是否是一个有效的解决方案。

这个 Github 问题可能对您有所帮助:https://github.com/aspnet/Security/issues/830。 - adem caglin
1个回答

11

我能在一个实现了ITicketStore接口的类中使用MemoryCache来存储AuthenticationTicket吗?

当然可以,以下是我使用了近一年的实现代码。

app.UseCookieAuthentication(new CookieAuthenticationOptions
{
    AuthenticationScheme = "App.Cookie",
    AutomaticAuthenticate = true,
    AutomaticChallenge = true,
    LoginPath = new PathString("/Authentication/SignIn"),
    LogoutPath = new PathString("/Authentication/SignOut"),
    ReturnUrlParameter = "/Authentication/SignIn",
    SessionStore = new MemoryCacheStore(cache)
});

MemoryCacheStore 的实现看起来像这样,并且遵循了您分享的示例:

public class MemoryCacheStore : ITicketStore
{
    private const string KeyPrefix = "AuthSessionStore-;
    private readonly IMemoryCache _cache;

    public MemoryCacheStore(IMemoryCache cache)
    {
        _cache = cache;
    }

    public async Task<string> StoreAsync(AuthenticationTicket ticket)
    {
        var key = KeyPrefix + Guid.NewGuid();
        await RenewAsync(key, ticket);
        return key;
    }

    public Task RenewAsync(string key, AuthenticationTicket ticket)
    {
        // https://github.com/aspnet/Caching/issues/221
        // Set to "NeverRemove" to prevent undesired evictions from gen2 GC
        var options = new MemoryCacheEntryOptions
        {
            Priority = CacheItemPriority.NeverRemove
        };
        var expiresUtc = ticket.Properties.ExpiresUtc;

        if (expiresUtc.HasValue)
        {
            options.SetAbsoluteExpiration(expiresUtc.Value);
        }    

        options.SetSlidingExpiration(TimeSpan.FromMinutes(60));

        _cache.Set(key, ticket, options);

        return Task.FromResult(0);
    }

    public Task<AuthenticationTicket> RetrieveAsync(string key)
    {
        AuthenticationTicket ticket;
        _cache.TryGetValue(key, out ticket);
        return Task.FromResult(ticket);
    }

    public Task RemoveAsync(string key)
    {
        _cache.Remove(key);
        return Task.FromResult(0);
    }
}

谢谢回复。与使用cookies相比,这种方法有什么不足之处吗?我能想到的唯一问题是Web应用程序可能会耗尽内存。 - jasdefer
没有内存确实不是一个问题。我只是在考虑一般的缺点。是的,它使用cookies,但AuthenticationTicket本身并没有存储在cookie中,只有引用。这是正确的吗? - jasdefer
2
不正确,cookie 中的所有内容都会反序列化为 ClaimsPrinicipal,而该原则构成了 AuthenticationTicket https://github.com/aspnet/Security/blob/22d2fe99c6fd9806b36025399a217a3a8b4e50f4/src/Microsoft.AspNetCore.Authentication/AuthenticationTicket.cs。 - David Pine
除了AuthenticationTicketMemoryCacheStore之外,框架会处理这部分内容,并正确调用RetrieveAsyncRenewAsync - David Pine
当我使用这种方法登录时,我的“用户”为“null”。我错过了什么?身份验证 cookie 如预期创建。 - djack109
显示剩余2条评论

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