我正在使用ASP.NET 5附带的内置UserManager
类在我的站点上实现重置密码功能。
在我的开发环境中一切正常。但是,一旦我在作为Azure网站运行的生产站点中尝试它,我会得到以下异常:
System.Security.Cryptography.CryptographicException:数据保护操作失败。这可能是由于没有为当前线程的用户上下文加载用户配置文件引起的,这可能是线程模拟时的情况。
这就是我设置UserManager
实例的方法:
var provider = new Microsoft.Owin.Security.DataProtection.DpapiDataProtectionProvider(SiteConfig.SiteName);
UserManager.UserTokenProvider = new Microsoft.AspNet.Identity.Owin.DataProtectorTokenProvider<User>(provider.Create(ResetPasswordPurpose));
然后,我生成令牌(通过电子邮件发送给用户,以便他们可以验证他们确实想要重置密码):
string token = UserManager.GeneratePasswordResetToken(user.Id);
不幸的是,当它在Azure上运行时,我得到了上面的例外。
我用Google搜索并找到了这个possible solution。但是,它根本不起作用,我仍然得到同样的例外。
根据该链接,它与会话令牌有关,这些会话令牌不在像Azure这样的Web场上工作。
DpapiDataProtectionProvider使用DPAPI,它无法在Web场/云环境中正常工作,因为加密数据只能由加密数据解密。您需要的是一种加密数据的方法,使其可以被您环境中的任何计算机解密。遗憾的是,除了DpapiDataProtectionProvider之外,ASP.NET Identity 2.0不包含任何其他IProtectionProvider实现。但是,推出自己的并不太难。
一种选择是使用the MachineKey class如下:
public class MachineKeyProtectionProvider : IDataProtectionProvider
{
public IDataProtector Create(params string[] purposes)
{
return new MachineKeyDataProtector(purposes);
}
}
public class MachineKeyDataProtector : IDataProtector
{
private readonly string[] _purposes;
public MachineKeyDataProtector(string[] purposes)
{
_purposes = purposes;
}
public byte[] Protect(byte[] userData)
{
return MachineKey.Protect(userData, _purposes);
}
public byte[] Unprotect(byte[] protectedData)
{
return MachineKey.Unprotect(protectedData, _purposes);
}
}
要使用此选项,您需要执行几个步骤。
步骤1
修改您的代码以使用MachineKeyProtectionProvider。
using Microsoft.AspNet.Identity.Owin;
// ...
var provider = new MachineKeyProtectionProvider();
UserManager.UserTokenProvider = new DataProtectorTokenProvider<User>(
provider.Create("ResetPasswordPurpose"));
第2步
Synchronize the MachineKey在Web场/云环境中的所有计算机上都有价值。这听起来很可怕,但是为了让ViewState验证在Web场中正常工作(它也使用DPAPI),我们之前无数次执行过相同的步骤。
考虑使用IAppBuilder.GetDataProtectionProvider()
而不是声明一个新的DpapiDataProtectionProvider
。
与您类似,我通过从我找到的代码示例中配置我的UserManager来介绍此问题:
public class UserManager : UserManager<ApplicationUser>
{
public UserManager() : base(new UserStore<ApplicationUser>(new MyDbContext()))
{
// this does not work on azure!!!
var provider = new Microsoft.Owin.Security.DataProtection.DpapiDataProtectionProvider("ASP.NET IDENTITY");
this.UserTokenProvider = new DataProtectorTokenProvider<ApplicationUser>(provider.Create("EmailConfirmation"))
{
TokenLifespan = TimeSpan.FromHours(24),
};
}
}
与上面链接的CodePlex问题实际上引用了blog post,它已经用更简单的问题解决方案进行了更新。它建议保存对IDataProtector
的静态引用...
public partial class Startup
{
internal static IDataProtectionProvider DataProtectionProvider { get; private set; }
public void ConfigureAuth(IAppBuilder app)
{
DataProtectionProvider = app.GetDataProtectionProvider();
// other stuff.
}
}
...然后从UserManager中引用它
public class UserManager : UserManager<ApplicationUser>
{
public UserManager() : base(new UserStore<ApplicationUser>(new MyDbContext()))
{
var dataProtectionProvider = Startup.DataProtectionProvider;
this.UserTokenProvider =
new DataProtectorTokenProvider<ApplicationUser>(dataProtectionProvider.Create("ASP.NET Identity"));
// do other configuration
}
}
johnso的答案也提供了一个很好的例子,说明如何使用Autofac进行连接。
我有同样的问题,除了我在亚马逊ec2主办。 我能够通过转到IIS中的应用程序池和(在右键单击后的高级设置下)设置流程模型来解决它 - 加载用户配置文件= true。
我遇到了同样的问题(在Azure上运行时Owin.Security.DataProtection.DpapiDataProtectionProvider
失败),而且Staley是正确的,你不能使用DpapiDataProtectionProvider
。
如果你正在使用OWIN Startup Classes你可以避免滚动自己的IDataProtectionProvider
,而是使用GetDataProtectionProvider
的IAppBuilder
方法。
例如,使用Autofac:
internal static IDataProtectionProvider DataProtectionProvider;
public void ConfigureAuth(IAppBuilder app)
{
// ...
DataProtectionProvider = app.GetDataProtectionProvider();
builder.Register<IDataProtectionProvider>(c => DataProtectionProvider)
.InstancePerLifetimeScope();
// ...
}