Daa*_*ath 2 asp.net cookies asp.net-core asp.net-core-2.0
tl;博士
我应该在我的 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>();
});
Run Code Online (Sandbox Code Playgroud)
票务商店
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;
}
Run Code Online (Sandbox Code Playgroud)
我也遇到了这个问题。
Microsoft.Owin.Security.Cookies 中的 SessionIdClaim 值为“Microsoft.Owin.Security.Cookies-SessionId”,而 Microsoft.AspNetCore.Authentication.Cookies 中的 SessionIdClaim 值为“Microsoft.AspNetCore.Authentication.Cookies-SessionId”。
即使您实现了分布式会话存储(例如使用 RedisCacheTicketStore),由于 AspNetCore 端的此代码,这会导致 SessionId Missing 错误,如下所述:https ://mikerussellnz.github.io/.NET-Core-Auth-票务-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);
}
Run Code Online (Sandbox Code Playgroud)
使用这些附加方法,可以将 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);
}
Run Code Online (Sandbox Code Playgroud)
我不确定这是否是最好的方法,但它似乎适用于我的测试项目,不可否认,我没有在生产中使用它,希望这会有所帮助。
更新 #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);
}
}
}
}
Run Code Online (Sandbox Code Playgroud)
如果有更好的方法,我很想知道,或者有更好的地方来换掉 cookie。
正如其他答案所指出的那样,问题在于 Owin cookie 的会话密钥声明具有另一种类型字符串,而不是 ASP.Net Core 中预期的字符串。
以下票证数据格式的实现确保在生成 cookie 字符串时添加 ASP.Net Core 的会话密钥声明。
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);
}
}
Run Code Online (Sandbox Code Playgroud)
应将此代码添加到 ASP.Net Framework 项目中。您可以在代码中使用它而不是 AspNetTicketDataFormat StartUp.cs,如下所示:
app.UseCookieAuthentication(new CookieAuthenticationOptions {
TicketDataFormat = new AspNetCoreCompatibleTicketDataFormat(
new DataProtectorShim(...
Run Code Online (Sandbox Code Playgroud)
该代码确保生成的 cookie 包含 ASP.NET Core 已知的会话 ID 声明。它适用于在 ASP.NET Framework OWIN 项目中生成 cookie 并在 ASP.NET Core 项目中使用它的场景。
必须确保始终添加两者,才能使其在 ASP.NET Core 项目中生成 cookie 的相反情况下正常工作。
| 归档时间: |
|
| 查看次数: |
8106 次 |
| 最近记录: |