tl;博士
- 拥有 .NET Core 2.0 应用程序,该应用程序使用数据保护提供程序,在我域中的所有网站上保留一个 key 文件。
- 工作正常,但是,应用程序 cookie 变得太大。
- 使用 ITicketStore 在 cookie 上实现了 SessionStore
- Cookie 的大小大大减少了,但是,来自 DPP 的 key 不再在我的网站上保留。
我应该在我的 ITicketStore 实现中做些什么来解决这个问题吗?我假设是这样,因为这是出现问题的地方,但是,我无法弄清楚。
一些片段:
Startup.cs --> ConfigureServices()
var keysFolder = $@"c:\temp\_WebAppKeys\{_env.EnvironmentName.ToLower()}";
var protectionProvider = DataProtectionProvider.Create(new DirectoryInfo(keysFolder));
var dataProtector = protectionProvider.CreateProtector(
"Microsoft.AspNetCore.Authentication.Cookies.CookieAuthenticationMiddleware",
"Cookies",
"v2");
--snip--
services.AddSingleton<ITicketStore, TicketStore>();
--snip--
services.AddDataProtection()
.PersistKeysToFileSystem(new DirectoryInfo(keysFolder))
.SetApplicationName("app_auth");
services.ConfigureApplicationCookie(options =>
{
options.Cookie.Name = ".XAUTH";
options.Cookie.Domain = ".domain.com";
options.ExpireTimeSpan = TimeSpan.FromDays(7);
options.LoginPath = "/Account/Login";
options.DataProtectionProvider = protectionProvider;
options.TicketDataFormat = new TicketDataFormat(dataProtector);
options.CookieManager = new ChunkingCookieManager();
options.SessionStore = services.BuildServiceProvider().GetService<ITicketStore>();
});
TicketStore.cs
public class TicketStore : ITicketStore
{
private IMemoryCache _cache;
private const string KeyPrefix = "AuthSessionStore-";
public TicketStore(IMemoryCache cache)
{
_cache = cache;
}
public Task RemoveAsync(string key)
{
_cache.Remove(key);
return Task.FromResult(0);
}
public Task RenewAsync(string key, AuthenticationTicket ticket)
{
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 async Task<string> StoreAsync(AuthenticationTicket ticket)
{
var key = KeyPrefix + Guid.NewGuid();
await RenewAsync(key, ticket);
return key;
}
最佳答案
我也遇到了这个问题。
Microsoft.Owin.Security.Cookies中的SessionIdClaim值为“Microsoft.Owin.Security.Cookies-SessionId”,而Microsoft.AspNetCore.Authentication.Cookies中的SessionIdClaim值为“Microsoft.AspNetCore.Authentication.Cookies-SessionId ".
即使您实现了分布式 session 存储(例如使用 RedisCacheTicketStore),也会由于 AspNetCore 端的此代码而导致 SessionId Missing 错误,如下所述:https://mikerussellnz.github.io/.NET-Core-Auth-Ticket-Redis/
我能够使用新字符串重新编译 AspNetKatana 项目,然后在 .NET Core 端找到了 SessionID。
此外,似乎 AuthenticationTicket 类不同,所以我能够通过实现一个转换方法来转换 Microsoft.Owin.Security.AuthenticationTicket 票证到 Microsoft.AspNetCore.Authentication.AuthenticationTicket 票证,然后使用 AspNetCore 序列化程序 (Microsoft.AspNetCore.Authentication.TicketSerializer) 存储票证。
public Microsoft.AspNetCore.Authentication.AuthenticationTicket ConvertTicket(Microsoft.Owin.Security.AuthenticationTicket ticket)
{
Microsoft.AspNetCore.Authentication.AuthenticationProperties netCoreAuthProps = new Microsoft.AspNetCore.Authentication.AuthenticationProperties();
netCoreAuthProps.IssuedUtc = ticket.Properties.IssuedUtc;
netCoreAuthProps.ExpiresUtc = ticket.Properties.ExpiresUtc;
netCoreAuthProps.IsPersistent = ticket.Properties.IsPersistent;
netCoreAuthProps.AllowRefresh = ticket.Properties.AllowRefresh;
netCoreAuthProps.RedirectUri = ticket.Properties.RedirectUri;
ClaimsPrincipal cp = new ClaimsPrincipal(ticket.Identity);
Microsoft.AspNetCore.Authentication.AuthenticationTicket netCoreTicket = new Microsoft.AspNetCore.Authentication.AuthenticationTicket(cp, netCoreAuthProps, "Cookies");
return netCoreTicket;
}
private static Microsoft.AspNetCore.Authentication.TicketSerializer _netCoreSerializer = Microsoft.AspNetCore.Authentication.TicketSerializer.Default;
private static byte[] SerializeToBytesNetCore(Microsoft.AspNetCore.Authentication.AuthenticationTicket source)
{
return _netCoreSerializer.Serialize(source);
}
有了这些额外的方法,RenwAsync 方法可以改成这样:
public Task RenewAsync(string key, Microsoft.Owin.Security.AuthenticationTicket ticket)
{
var options = new DistributedCacheEntryOptions();
var expiresUtc = ticket.Properties.ExpiresUtc;
if (expiresUtc.HasValue)
{
options.SetAbsoluteExpiration(expiresUtc.Value);
}
var netCoreTicket = ConvertTicket(ticket);
// convert to .NET Core format
byte[] netCoreVal = SerializeToBytesNetCore(netCoreTicket);
// serialize ticket using .NET Core Serializer
_cache.Set(key, netCoreVal, options);
return Task.FromResult(0);
}
我不确定这是否是最好的方法,但它似乎适用于我的测试项目,诚然我没有在生产中使用它,希望它能有所帮助。
更新 #1:避免重新编译的替代方法
看起来这也可以通过在 OWIN 端使用两个 SessionId 声明值重新创建 cookie 来实现。这将允许您在不重新编译的情况下使用标准库。我今天早上试过了,但还没有机会彻底测试它,尽管在我最初的测试中它确实在两边正确地加载了声明。基本上,如果您将身份验证票证修改为具有两个 SessionId 声明,它将在两个应用程序中找到该 session 。此代码片段获取 cookie,对其取消保护,添加附加声明,然后替换 CookieAuthenticationProvider 的 OnValidateIdentity 事件中的 cookie。
string cookieName = "myappname";
string KatanaSessionIdClaim = "Microsoft.Owin.Security.Cookies-SessionId";
string NetCoreSessionIdClaim = "Microsoft.AspNetCore.Authentication.Cookies-SessionId";
Microsoft.Owin.Security.Interop.ChunkingCookieManager cookieMgr = new ChunkingCookieManager();
OnValidateIdentity = ctx =>
{
var incomingIdentity = ctx.Identity;
var cookie = cookieMgr.GetRequestCookie(ctx.OwinContext, cookieName);
if (cookie != null)
{
var ticket = TicketDataFormat.Unprotect(cookie);
if (ticket != null)
{
Claim claim = ticket.Identity.Claims.FirstOrDefault(c => c.Type.Equals(KatanaSessionIdClaim));
Claim netCoreSessionClaim = ticket.Identity.Claims.FirstOrDefault(c => c.Type.Equals(NetCoreSessionIdClaim));
if (netCoreSessionClaim == null)
{
// adjust cookie options as needed.
CookieOptions opts = new CookieOptions();
opts.Expires = ticket.Properties.ExpiresUtc == null ?
DateTime.Now.AddDays(14) : ticket.Properties.ExpiresUtc.Value.DateTime;
opts.HttpOnly = true;
opts.Path = "/";
opts.Secure = true;
netCoreSessionClaim = new Claim(NetCoreSessionIdClaim, claim.Value);
ticket.Identity.AddClaim(netCoreSessionClaim);
string newCookieValue = TicketDataFormat.Protect(ticket);
cookieMgr.DeleteCookie(ctx.OwinContext, cookieName, opts);
cookieMgr.AppendResponseCookie(ctx.OwinContext, cookieName, newCookieValue, opts);
}
}
}
}
如果有更好的方法,我很想知道,或者换出 cookie 的更好地方。
关于asp.net - 将 SessionStore (ITicketStore) 添加到我的应用程序 cookie 会使我的数据保护提供程序无法工作,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/48510143/