Asp.net 将SessionStore(ITicketStore)添加到我的应用程序cookie会使我的数据保护提供程序无法工作
tl;博士Asp.net 将SessionStore(ITicketStore)添加到我的应用程序cookie会使我的数据保护提供程序无法工作,asp.net,cookies,asp.net-core,asp.net-core-2.0,Asp.net,Cookies,Asp.net Core,Asp.net Core 2.0,tl;博士 拥有.NET Core 2.0应用程序,该应用程序使用数据保护提供程序,在我的域中的所有站点上持久保存密钥文件 工作正常,但应用程序cookie变得太大 使用ITicketStore在cookie上实现会话存储 Cookie的大小大大减小,但是,来自DPP的密钥不再在我的站点中存在 在我的ITicketStore实现中,我应该做些什么来解决这个问题吗?我假设是这样的,因为这就是问题产生的地方,然而,我无法解决它 一些片段: Startup.cs-->配置服务() var key
- 拥有.NET Core 2.0应用程序,该应用程序使用数据保护提供程序,在我的域中的所有站点上持久保存密钥文件
- 工作正常,但应用程序cookie变得太大
- 使用ITicketStore在cookie上实现会话存储
- Cookie的大小大大减小,但是,来自DPP的密钥不再在我的站点中存在
Startup.cs-->配置服务()
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>();
});
var keysFolder=$@“c:\temp\\u WebAppKeys\{{u env.EnvironmentName.ToLower()}”;
var protectionProvider=DataProtectionProvider.Create(newdirectoryinfo(keysFolder));
var dataProtector=protectionProvider.CreateProtector(
“Microsoft.AspNetCore.Authentication.Cookies.CookieAuthenticationMiddleware”,
“饼干”,
“v2”);
--剪断--
services.AddSingleton();
--剪断--
services.AddDataProtection()
.PersistKeySystem(新目录信息(密钥文件夹))
.SetApplicationName(“app_auth”);
services.configureApplicationOK(选项=>
{
options.Cookie.Name=“.XAUTH”;
options.Cookie.Domain=“.Domain.com”;
options.ExpireTimeSpan=TimeSpan.FromDays(7);
options.LoginPath=“/Account/Login”;
options.DataProtectionProvider=protectionProvider;
options.TicketDataFormat=新的TicketDataFormat(dataProtector);
options.CookieManager=新建ChunkingCookieManager();
options.SessionStore=services.BuildServiceProvider().GetService();
});
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;
}
public class TicketStore:ITicketStore
{
私有IMemoryCache\u缓存;
private const string KeyPrefix=“AuthSessionStore-”;
公共票证存储(IMemoryCache缓存)
{
_缓存=缓存;
}
公共任务RemoveAsync(字符串键)
{
_缓存。删除(键);
返回Task.FromResult(0);
}
公共任务续订异步(字符串密钥、身份验证票证)
{
var options=newmemorycacheentryoptions
{
优先级=CacheItemPriority.NeverRemove
};
var expiresUtc=ticket.Properties.expiresUtc;
if(expiresUtc.HasValue)
{
options.SetAbsoluteExpiration(expiresUtc.Value);
}
选项。设置LidingExpiration(TimeSpan.FromMinutes(60));
_cache.Set(键、票据、选项);
返回Task.FromResult(0);
}
公共任务检索同步(字符串键)
{
认证票证;
_cache.TryGetValue(key,out-ticket);
返回任务.FromResult(票证);
}
公共异步任务StoreAsync(身份验证票证)
{
var key=KeyPrefix+Guid.NewGuid();
等待续订异步(密钥、票证);
返回键;
}
我也遇到了这个问题
Microsoft.Owin.Security.Cookies中的sessiondClaim值为“Microsoft.Owin.Security.Cookies SessionId”,而Microsoft.AspNetCore.Authentication.Cookies中的sessiondClaim值为“Microsoft.AspNetCore.Authentication.Cookies SessionId”
即使您实现了分布式会话存储(例如使用RedisCacheTicketStore),这也会导致AspNetCore端由于此代码而导致SessionId丢失错误,如下所述:
我能够用新字符串重新编译AspNetKatana项目,然后在.NET核心端找到SessionID
此外,AuthenticationTicket类似乎不同,因此我通过实现转换方法来转换
Microsoft.Owin.Security.AuthenticationTicket票证到Microsoft.AspNetCore.Authentication.AuthenticationTicket票证,然后使用AspNetCore序列化程序(Microsoft.AspNetCore.Authentication.TicketSerializer)存储票证
使用这些附加方法,可以将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声明,它将在两个应用程序中找到会话。此代码段获取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。我也遇到了这个问题 Microsoft.Owin.Security.Cookies中的sessiondClaim值为“Microsoft.Owin.Security.Cookies SessionId”,而Microsoft.AspNetCore.Authentication.Cookies中的sessiondClaim值为“Microsoft.AspNetCore.Authentication.Cookies SessionId” 即使您实现了分布式会话存储(例如使用RedisCacheTicketStore),这也会导致AspNetCore端由于此代码而导致SessionId丢失错误,如下所述: 我能够用新字符串重新编译AspNetKatana项目,然后在.NET核心端找到SessionID 此外,AuthenticationTicket类似乎不同,因此我通过实现转换方法来转换 Microsoft.Owin.Security.AuthenticationTicket票证到Microsoft.AspNetCore.Authentication.AuthenticationTicket票证,然后使用AspNetCore序列化程序(Microsoft.AspNetCore.Authentication)存储票证
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);
}
}
}
}
public class OwinAspNetCompatibleCookieManager : ICookieManager
{
private const string OwinSessionIdClaim = "Microsoft.Owin.Security.Cookies-SessionId";
private const string AspNetCoreSessionIdClaim = "Microsoft.AspNetCore.Authentication.Cookies-SessionId";
private readonly ICookieManager actualCookieManager;
public OwinAspNetCompatibleCookieManager(ICookieManager actualCookieManager) => this.actualCookieManager = actualCookieManager;
// TODO oh this async void is so so bad, i have to find another way
public async void AppendResponseCookie(HttpContext context, string key, string value, CookieOptions options)
{
IAuthenticationHandler handler = await context.RequestServices.GetRequiredService<IAuthenticationHandlerProvider>().GetHandlerAsync(context, CookieAuthenticationDefaults.AuthenticationScheme).ConfigureAwait(false);
if (handler is CookieAuthenticationHandler cookieHandler)
{
value = MakeOwinAspNetCoreCompatible(key, value, cookieHandler.Options);
}
actualCookieManager.AppendResponseCookie(context, key, value, options);
}
public void DeleteCookie(HttpContext context, string key, CookieOptions options)
{
actualCookieManager.DeleteCookie(context, key, options);
}
public string GetRequestCookie(HttpContext context, string key)
{
return actualCookieManager.GetRequestCookie(context, key);
}
private string MakeOwinAspNetCoreCompatible(string key, string cookieValue, CookieAuthenticationOptions options)
{
if (key.Equals("MySharedCookieName") && !string.IsNullOrWhiteSpace(cookieValue))
{
AuthenticationTicket ticket = options.TicketDataFormat.Unprotect(cookieValue);
ClaimsPrincipal principal = ticket.Principal;
Claim aspNetCoreClaim = ticket.Principal.Claims.FirstOrDefault(x => x.Type.Equals(AspNetCoreSessionIdClaim));
Claim owinClaim = ticket.Principal.Claims.FirstOrDefault(x => x.Type.Equals(OwinSessionIdClaim));
Claim[] claims = null;
if (aspNetCoreClaim != null && owinClaim == null)
{
claims = new Claim[] { aspNetCoreClaim, new Claim(OwinSessionIdClaim, aspNetCoreClaim.Value) };
}
else if (aspNetCoreClaim == null && owinClaim != null)
{
claims = new Claim[] { owinClaim, new Claim(AspNetCoreSessionIdClaim, owinClaim.Value) };
}
if (claims?.Length > 0)
{
var newIdentity = new ClaimsIdentity(claims, principal.Identity.AuthenticationType);
principal = new ClaimsPrincipal(newIdentity);
ticket = new AuthenticationTicket(principal, ticket.AuthenticationScheme);
cookieValue = options.TicketDataFormat.Protect(ticket);
}
}
return cookieValue;
}
}
...
options.CookieManager = new OwinAspNetCompatibleCookieManager(new ChunkingCookieManager());
...
public class AspNetCoreCompatibleTicketDataFormat : ISecureDataFormat<AuthenticationTicket> {
private const string OwinSessionIdClaim = "Microsoft.Owin.Security.Cookies-SessionId";
private const string AspNetCoreSessionIdClaim = "Microsoft.AspNetCore.Authentication.Cookies-SessionId";
private readonly ISecureDataFormat<AuthenticationTicket> dataFormat;
public AspNetCoreCompatibleTicketDataFormat(IDataProtector protector) {
this.dataFormat = new AspNetTicketDataFormat(protector);
}
public string Protect(AuthenticationTicket data) {
var sessionClaim = data.Identity.FindFirst(OwinSessionIdClaim);
if (sessionClaim != null) {
data.Identity.AddClaim(new Claim(AspNetCoreSessionIdClaim, sessionClaim.Value));
}
return this.dataFormat.Protect(data);
}
public AuthenticationTicket Unprotect(string protectedText) {
return this.dataFormat.Unprotect(protectedText);
}
}
app.UseCookieAuthentication(new CookieAuthenticationOptions {
TicketDataFormat = new AspNetCoreCompatibleTicketDataFormat(
new DataProtectorShim(...