ASP.NET 5に付属の組み込みUserManager
クラスを使用して、サイトにパスワードのリセット機能を実装しています。
私の開発環境ではすべてが正常に機能します。ただし、Azure Webサイトとして実行されている運用サイトで試してみると、次の例外が発生します。
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で実行すると、上記の例外が発生します。
私はグーグルで調べて、これを見つけました 可能な解決策 。ただし、まったく機能せず、同じ例外が発生します。
リンクによると、AzureのようなWebファームで動作しないセッショントークンと関係があります。
DpapiDataProtectionProviderは [〜#〜] dpapi [〜#〜] を使用します。これは、暗号化されたデータは暗号化されたマシンでのみ復号化できるため、Webファーム/クラウド環境では正常に動作しません。必要なのは、環境内の任意のマシンで解読できるようにデータを暗号化する方法です。残念ながら、ASP.NET Identity 2.0には、DpapiDataProtectionProvider以外のIProtectionProviderの実装は含まれていません。ただし、独自にロールバックするのはそれほど難しくありません。
1つのオプションは、次のように MachineKeyクラス を使用することです。
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
MachineKeyの同期 Webファーム/クラウド環境のすべてのマシンの値。これは怖いように聞こえますが、ViewStateの検証をWebファームで適切に機能させるために(DPAPIを使用するために)何回も実行したのと同じステップです。
新しいDpapiDataProtectionProvider
を宣言する代わりに、IAppBuilder.GetDataProtectionProvider()
を使用することを検討してください。
あなたと同様に、見つけたコードサンプルから、このように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の問題は、実際には ブログ投稿 を参照しており、問題に対するより簡単な解決策で更新されています。 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を使用してこれを接続する方法の良い例を提供します。
Amazon ec2でホストしていたことを除いて、同じ問題がありました。
IISおよび(右クリック後の詳細設定の下で)]プロセスモデルの設定-ユーザープロファイルのロード= trueでアプリケーションプールに移動して解決できました。
私は同じ問題を抱えていました(Owin.Security.DataProtection.DpapiDataProtectionProvider
Azureで実行すると失敗します。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();
// ...
}