我对使用相同数据库的2个api有特殊要求。如果我使用VS2013中的默认模板创建项目并从同一台机器上的其他api传递访问令牌,它将正确解密并加载用户。然而第二个我打开nuget包管理器控制台并输入“Update-Package”来更新所有模板的包,它不再解密访问令牌并返回未经授权的访问?
在我从中消除所有模板膨胀之前,我想更新它。有什么变化,有没有办法对它进行编码,以便在我更新后正常工作?
答案 0 :(得分:3)
我本周刚刚解决了这个问题。我的猜测是你从3.0.0更新到3.0.1(谈论Microsoft.Owin.Security
)。
程序集包含一个名为TokenSerializer
的类。如果您反编译代码,您将看到它具有内部格式版本。在3.0.0中,内部格式为" 2"。在3.0.1中,他们将内部格式版本提升为" 3"。这会导致.Deserialize
方法保释。除了打破语义版本控制的愚蠢之外,序列化和反序列化方法是相同的,这意味着它们可以更改代码以接受格式或为我们实现迁移。< / p>
我最终做的是将3.0.0版本的代码作为一个新类内联(因为我无法在同一个进程中托管两个程序集并且站起来一个新进程太麻烦了)而且我只需手动尝试反序列化两者。工作正常,但肯定很讨厌。
使用代码进行更新:
public class RefreshTokenTicketRepository : AzureTableStorageRepository<RefreshTokenTicketDto>, IRefreshTokenTicketRepository
{
private static readonly TicketSerializer _ticketSerializer = new TicketSerializer();
private static readonly TicketSerializer_v2 _ticketSerializer_v2 = new TicketSerializer_v2();
private const string tableName = "refreshtokentickets";
public const string PartitionKey = "refreshtokentickets";
public enum SerializationFormatVersion
{
vCurrent,
v2,
Unknown
}
private readonly ILogger _logger;
public RefreshTokenTicketRepository(IHoursTrackerCloudStorageAccountFactory storageAccountFactory, ILogger logger)
: base(storageAccountFactory, tableName)
{
_logger = logger.ForContext<RefreshTokenTicketRepository>();
}
public async Task<AuthenticationTicket> GetAsync(string refreshToken)
{
using (LogContext.PushProperty("Refresh Token", refreshToken))
{
if (String.IsNullOrEmpty(refreshToken))
{
_logger.Debug("Null or empty refresh token");
return null;
}
RefreshTokenTicketDto refreshTokenTicketDto = await GetAsync(PartitionKey, refreshToken);
if (refreshTokenTicketDto == null)
{
_logger.Debug("Authentication ticket not found");
return null;
}
SerializationFormatVersion version;
AuthenticationTicket authenticationTicket = Deserialize(refreshTokenTicketDto.RefreshTokenTicket, out version);
if (authenticationTicket != null && version != SerializationFormatVersion.vCurrent)
{
_logger.Information("Updating authentication ticket serialization format from {Version}", version);
await InsertOrReplaceAsync(new RefreshTokenTicketDto(refreshToken, _ticketSerializer.Serialize(authenticationTicket)));
}
return authenticationTicket;
}
}
public Task PersistAsync(string refreshToken, AuthenticationTicket refreshTokenTicket)
{
return PersistAsync(new RefreshTokenTicketDto(refreshToken, _ticketSerializer.Serialize(refreshTokenTicket)));
}
/// <summary>
/// In Microsoft.Owin.Security 3.0.1, Microsoft changed the internal version format from 2 to 3.
/// This caused the TokenSerializer to return null for otherwise valid AuthenticationTickets.
/// So we decompiled the v2 implementation and inlined it below and update the the bytes to reflect v3.
/// </summary>
/// <param name="authenticationTicketBytes"></param>
/// <param name="version">The version of the AuthenticationTicket</param>
/// <returns></returns>
public AuthenticationTicket Deserialize(byte[] authenticationTicketBytes, out SerializationFormatVersion version)
{
_logger.Debug("Deserializing authentication ticket");
AuthenticationTicket authenticationTicket = _ticketSerializer.Deserialize(authenticationTicketBytes);
if (authenticationTicket != null)
{
version = SerializationFormatVersion.vCurrent;
_logger.Debug("Authentication ticket version is {Version}", version);
return authenticationTicket;
}
authenticationTicket = _ticketSerializer_v2.Deserialize(authenticationTicketBytes);
if (authenticationTicket != null)
{
version = SerializationFormatVersion.v2;
_logger.Debug("Authentication ticket version is {Version}", version);
return authenticationTicket;
}
version = SerializationFormatVersion.Unknown;
_logger.Debug("Authentication ticket version is {Version}", version);
return null;
}
public byte[] Serialize(AuthenticationTicket authenticationTicket)
{
return _ticketSerializer.Serialize(authenticationTicket);
}
public byte[] Serialize_v2(AuthenticationTicket authenticationTicket)
{
return _ticketSerializer_v2.Serialize(authenticationTicket);
}
#region Decompiled Microsoft.Owin.Security.DataHandler.Serializer.TicketSerializer from Microsoft.Owin.Security 3.0.0
private class TicketSerializer_v2 : IDataSerializer<AuthenticationTicket>
{
private const int FormatVersion = 2;
public byte[] Serialize(AuthenticationTicket model)
{
using (var memoryStream = new MemoryStream())
{
using (var gzipStream = new GZipStream(memoryStream, CompressionLevel.Optimal))
{
using (var writer = new BinaryWriter(gzipStream))
{
Write(writer, model);
}
}
return memoryStream.ToArray();
}
}
public AuthenticationTicket Deserialize(byte[] data)
{
using (var memoryStream = new MemoryStream(data))
{
using (var gzipStream = new GZipStream(memoryStream, CompressionMode.Decompress))
{
using (var reader = new BinaryReader(gzipStream))
{
return Read(reader);
}
}
}
}
private static void Write(BinaryWriter writer, AuthenticationTicket model)
{
if (writer == null)
{
throw new ArgumentNullException("writer");
}
if (model == null)
{
throw new ArgumentNullException("model");
}
writer.Write(FormatVersion);
ClaimsIdentity identity = model.Identity;
writer.Write(identity.AuthenticationType);
WriteWithDefault(writer, identity.NameClaimType, "http://schemas.xmlsoap.org/ws/2005/05/identity/claims/name");
WriteWithDefault(writer, identity.RoleClaimType, "http://schemas.microsoft.com/ws/2008/06/identity/claims/role");
writer.Write(identity.Claims.Count());
foreach (Claim claim in identity.Claims)
{
WriteWithDefault(writer, claim.Type, identity.NameClaimType);
writer.Write(claim.Value);
WriteWithDefault(writer, claim.ValueType, "http://www.w3.org/2001/XMLSchema#string");
WriteWithDefault(writer, claim.Issuer, "LOCAL AUTHORITY");
WriteWithDefault(writer, claim.OriginalIssuer, claim.Issuer);
}
var bootstrapContext = identity.BootstrapContext as BootstrapContext;
if (bootstrapContext == null || string.IsNullOrWhiteSpace(bootstrapContext.Token))
{
writer.Write(0);
}
else
{
writer.Write(bootstrapContext.Token.Length);
writer.Write(bootstrapContext.Token);
}
PropertiesSerializer.Write(writer, model.Properties);
}
private static AuthenticationTicket Read(BinaryReader reader)
{
if (reader == null)
{
throw new ArgumentNullException("reader");
}
if (reader.ReadInt32() != FormatVersion)
{
return null;
}
string authenticationType = reader.ReadString();
string str1 = ReadWithDefault(reader, "http://schemas.xmlsoap.org/ws/2005/05/identity/claims/name");
string roleType = ReadWithDefault(reader, "http://schemas.microsoft.com/ws/2008/06/identity/claims/role");
int length = reader.ReadInt32();
var claimArray = new Claim[length];
for (var index = 0; index != length; ++index)
{
string type = ReadWithDefault(reader, str1);
string str2 = reader.ReadString();
string valueType = ReadWithDefault(reader, "http://www.w3.org/2001/XMLSchema#string");
string str3 = ReadWithDefault(reader, "LOCAL AUTHORITY");
string originalIssuer = ReadWithDefault(reader, str3);
claimArray[index] = new Claim(type, str2, valueType, str3, originalIssuer);
}
var identity = new ClaimsIdentity(claimArray, authenticationType, str1, roleType);
if (reader.ReadInt32() > 0)
{
identity.BootstrapContext = new BootstrapContext(reader.ReadString());
}
AuthenticationProperties properties = PropertiesSerializer.Read(reader);
return new AuthenticationTicket(identity, properties);
}
private static void WriteWithDefault(BinaryWriter writer, string value, string defaultValue)
{
if (string.Equals(value, defaultValue, StringComparison.Ordinal))
{
writer.Write("\0");
}
else
{
writer.Write(value);
}
}
private static string ReadWithDefault(BinaryReader reader, string defaultValue)
{
string a = reader.ReadString();
if (string.Equals(a, "\0", StringComparison.Ordinal))
{
return defaultValue;
}
return a;
}
}
#endregion
}