我有两个bounded contexts:
前者是现有的成熟产品,但是,它缺乏架构(SmartUI)导致难以维护的代码库,现在更加明显可见的是可扩展性和可扩展性。
我们通过引入新的后端应用程序来迭代地解决这个问题 - 可以通过OWIN / WebAPI服务进行公开。
目前我们只想在新应用程序中利用cookie身份验证。最初,我认为使用基于FormsAuthenticationTicket的现有cookie身份验证/验证将是轻而易举的。显然这不是真的。
在我们的WebForms应用程序中,我们使用MachineKey来指定我们的decryptionKey和validationKey以支持我们的Web场。在.NET4中,如果我没有弄错的话,默认算法是AES。我认为如果默认设置不够,利用这些信息来构建我们自己的TicketDataFormat会很简单。
首先要学到的东西:
理想情况下,我们不打算将我们的主应用程序更新到.NET 4.5来替换cookie加密。有没有人知道如何将OWIN的CookieAuthentication与现有的FormsAuthenticationTicket集成?
我们尝试创建自定义:
IDataProtector
,SecureDataFormat<AuthenticationTicket>
,IDataSerializer<AuthenticationTicket>
实施。
IDataSerializer将负责FormsAuthenticationTicket和AuthenticationTicket之间的转换。
不幸的是,我无法找到有关微软机票加密的准确信息。以下是IDataProtector的示例构思:
public byte[] Unprotect(byte[] protectedData)
{
using (var crypto = new AesCryptoServiceProvider())
{
byte[] result = null;
const Int32 blockSize = 16;
crypto.KeySize = 192;
crypto.Key = "<MachineKey>".ToBytesFromHexadecimal();
crypto.IV = protectedData.Take(blockSize).ToArray();
crypto.Padding = PaddingMode.None; // This prevents a padding exception thrown.
using (var decryptor = crypto.CreateDecryptor(crypto.Key, crypto.IV))
using (var msDecrypt = new MemoryStream(protectedData.Skip(blockSize).Take(protectedData.Length - blockSize).ToArray()))
{
using (var csDecrypt = new CryptoStream(msDecrypt, decryptor, CryptoStreamMode.Read))
{
result = new byte[protectedData.Length - blockSize];
csDecrypt.Read(result, 0, result.Length);
}
}
return result;
}
}
这假设Microsoft将IV添加到字节数组中。这也假设MachineKey是使用的AES密钥。但是,我已经读过MS使用MachineKey作为密钥派生函数 - 考虑其他设置,如AppIsolation,AppVirtualLocation,AppId等。基本上,这是在黑暗中拍摄,我需要一些亮点!
我们当前的方法
我们目前正在使用辅助cookie进行原型设计,以便与现有的.ASPXAUTH一起为新的应用程序上下文建立标识。不幸的是,这意味着在AuthenticationTicket和FormsAuthenticationTicket中保持会话同步滑动。
相关帖子
Accepting ASP.NET Forms Authentication cookies in an OWIN-hosted SignalR implementation?
答案 0 :(得分:14)
最初是否可以使用&lt; machineKey&gt; app.config中的元素。进一步的原型设计表明,我可以使用以下代码在两个有界上下文之间成功共享一个FormsAuthenticationTicket。
理想情况下,我们将实施适当的授权服务器以启用OpenID Connect,Forms,WS-Fed等,并使两个应用程序都在承载令牌之外运行。但是,这在短期内很有效。希望这有帮助!
我已经测试并验证了两个应用程序的成功加密/解密,formauthticket超时的滑动。您应该注意ticketCompatibilityMode的web.config formsAuthentication设置。
appBuilder.UseCookieAuthentication(new CookieAuthenticationOptions
{
CookieName = FormsAuthentication.FormsCookieName,
CookieDomain = FormsAuthentication.CookieDomain,
CookiePath = FormsAuthentication.FormsCookiePath,
CookieSecure = CookieSecureOption.SameAsRequest,
AuthenticationMode = AuthenticationMode.Active,
ExpireTimeSpan = FormsAuthentication.Timeout,
SlidingExpiration = true,
AuthenticationType = "Forms",
TicketDataFormat = new SecureDataFormat<AuthenticationTicket>(
new FormsAuthenticationTicketSerializer(),
new FormsAuthenticationTicketDataProtector(),
new HexEncoder())
});
<!-- app.config for OWIN Host - Only used for compatibility with existing auth ticket. -->
<authentication mode="Forms">
<forms domain=".hostname.com" protection="All" ... />
</authentication>
<machineKey validationKey="..." decryptionKey="..." validation="SHA1" />
public class HexEncoder : ITextEncoder
{
public String Encode(Byte[] data)
{
return data.ToHexadecimal();
}
public Byte[] Decode(String text)
{
return text.ToBytesFromHexadecimal();
}
}
public class FormsAuthenticationTicketDataProtector : IDataProtector
{
public Byte[] Protect(Byte[] userData)
{
FormsAuthenticationTicket ticket;
using (var memoryStream = new MemoryStream(userData))
{
var binaryFormatter = new BinaryFormatter();
ticket = binaryFormatter.Deserialize(memoryStream) as FormsAuthenticationTicket;
}
if (ticket == null)
{
return null;
}
try
{
var encryptedTicket = FormsAuthentication.Encrypt(ticket);
return encryptedTicket.ToBytesFromHexadecimal();
}
catch
{
return null;
}
}
public Byte[] Unprotect(Byte[] protectedData)
{
FormsAuthenticationTicket ticket;
try
{
ticket = FormsAuthentication.Decrypt(protectedData.ToHexadecimal());
}
catch
{
return null;
}
if (ticket == null)
{
return null;
}
using (var memoryStream = new MemoryStream())
{
var binaryFormatter = new BinaryFormatter();
binaryFormatter.Serialize(memoryStream, ticket);
return memoryStream.ToArray();
}
}
}
public class FormsAuthenticationTicketSerializer : IDataSerializer<AuthenticationTicket>
{
public Byte[] Serialize(AuthenticationTicket model)
{
var userTicket = new FormsAuthenticationTicket(
2,
model.Identity.GetClaimValue<String>(CustomClaim.UserName),
new DateTime(model.Properties.IssuedUtc.Value.UtcDateTime.Ticks, DateTimeKind.Utc),
new DateTime(model.Properties.ExpiresUtc.Value.UtcDateTime.Ticks, DateTimeKind.Utc),
model.Properties.IsPersistent,
String.Format(
"AuthenticationType={0};SiteId={1};SiteKey={2};UserId={3}",
model.Identity.AuthenticationType,
model.Identity.GetClaimValue<String>(CustomClaim.SiteId),
model.Identity.GetClaimValue<String>(CustomClaim.SiteKey),
model.Identity.GetClaimValue<String>(CustomClaim.UserId)),
FormsAuthentication.FormsCookiePath);
using (var dataStream = new MemoryStream())
{
var binaryFormatter = new BinaryFormatter();
binaryFormatter.Serialize(dataStream, userTicket);
return dataStream.ToArray();
}
}
public AuthenticationTicket Deserialize(Byte[] data)
{
using (var dataStream = new MemoryStream(data))
{
var binaryFormatter = new BinaryFormatter();
var ticket = binaryFormatter.Deserialize(dataStream) as FormsAuthenticationTicket;
if (ticket == null)
{
return null;
}
var userData = ticket.UserData.ToNameValueCollection(';', '=');
var authenticationType = userData["AuthenticationType"];
var siteId = userData["SiteId"];
var siteKey = userData["SiteKey"];
var userId = userData["UserId"];
var claims = new[]
{
CreateClaim(CustomClaim.UserName, ticket.Name),
CreateClaim(CustomClaim.UserId, userId),
CreateClaim(CustomClaim.AuthenticationMethod, authenticationType),
CreateClaim(CustomClaim.SiteId, siteId),
CreateClaim(CustomClaim.SiteKey, siteKey)
};
var authTicket = new AuthenticationTicket(new UserIdentity(claims, authenticationType), new AuthenticationProperties());
authTicket.Properties.IssuedUtc = new DateTimeOffset(ticket.IssueDate);
authTicket.Properties.ExpiresUtc = new DateTimeOffset(ticket.Expiration);
authTicket.Properties.IsPersistent = ticket.IsPersistent;
return authTicket;
}
}
private Claim CreateClaim(String type, String value)
{
return new Claim(type, value, ClaimValueTypes.String, CustomClaim.Issuer);
}
}