简体   繁体   English

使用 OWIN / Katana 在 Azure 上进行数据保护操作不成功

[英]The data protection operation was unsuccessful on Azure using OWIN / Katana

I'm trying to implement password reset on an OWIN/Katana based ASP.NET MVC website running in Azure.我正在尝试在运行于 Azure 的基于 OWIN/Katana 的 ASP.NET MVC 网站上实施密码重置。

It works fine when run locally but fails in production.它在本地运行时工作正常但在生产中失败。

I create a UserToken Provider我创建了一个 UserToken Provider

userManager.UserTokenProvider = new DataProtectorTokenProvider<ApplicationUser>(provider.Create("PasswordReset"))

But when I attempt to generate the token as follows但是当我尝试如下生成令牌时

var resetToken = await UserManager.GeneratePasswordResetTokenAsync(user.Id);

I get following exception.我得到以下异常。

System.Security.Cryptography.CryptographicException: The data protection operation was unsuccessful. System.Security.Cryptography.CryptographicException:数据保护操作不成功。 This may have been caused by not having the user profile loaded for the current thread's user context, which may be the case when the thread is impersonating.这可能是由于没有为当前线程的用户上下文加载用户配置文件造成的,这可能是线程正在模拟时的情况。 at System.Security.Cryptography.ProtectedData.Protect(Byte[] userData, Byte[] optionalEntropy, DataProtectionScope scope) at System.Security.Cryptography.DpapiDataProtector.ProviderProtect(Byte[] userData) at System.Security.Cryptography.DataProtector.Protect(Byte[] userData) at Microsoft.Owin.Security.DataProtection.DpapiDataProtector.Protect(Byte[] userData) at Microsoft.AspNet.Identity.Owin.DataProtectorTokenProvider 2.d__0.MoveNext() --- End of stack trace from previous location where exception was thrown --- at System.Runtime.CompilerServices.TaskAwaiter.ThrowForNonSuccess(Task task) at System.Runtime.CompilerServices.TaskAwaiter.HandleNonSuccessAndDebuggerNotification(Task task) at Microsoft.AspNet.Identity.UserManager`2.d__e9.MoveNext()在 System.Security.Cryptography.ProtectedData.Protect(Byte[] userData, Byte[] optionalEntropy, DataProtectionScope 范围) 在 System.Security.Cryptography.DpapiDataProtector.ProviderProtect(Byte[] userData) (Byte[] userData) at Microsoft.Owin.Security.DataProtection.DpapiDataProtector.Protect(Byte[] userData) at Microsoft.AspNet.Identity.Owin.DataProtectorTokenProvider 2.d__0.MoveNext() --- 上一个堆栈跟踪结束引发异常的位置 --- 在 System.Runtime.CompilerServices.TaskAwaiter.ThrowForNonSuccess(任务任务) 在 System.Runtime.CompilerServices.TaskAwaiter.HandleNonSuccessAndDebuggerNotification(任务任务) 在 Microsoft.AspNet.Identity.UserManager`2.d__e9.MoveNext ()

I have the same problem when I try to generate token with ASP .Net identity and custom login function in Web API. 当我尝试在Web API中使用ASP .Net标识和自定义登录功能生成令牌时,我遇到了同样的问题。

"The data protection operation was unsuccessful. This may have been caused by not having the user profile loaded for the current thread's user context, which may be the case when the thread is impersonating." “数据保护操作不成功。这可能是由于没有为当前线程的用户上下文加载用户配置文件引起的,这可能是线程模拟时的情况。”

What I did is just simply create an Application Setting called WEBSITE_LOAD_USER_PROFILE in Microsoft Azure and set it to 1. That solution works for me. 我所做的只是在Microsoft Azure中创建一个名为WEBSITE_LOAD_USER_PROFILE的应用程序设置,并将其设置为1.该解决方案适用于我。

You can see the detail here 你可以在这里看到细节

Please see my my answer to this question. 请看我对这个问题的答案 A much simpler solution can be achieved by utilizing IAppBuilder.GetDataProtectionProvider() 利用IAppBuilder.GetDataProtectionProvider()可以实现更简单的解决方案

I found a solution. 我找到了解决方案。 I'm not exactly sure if all steps are necessary to it work, but now my app works perfectly: 我不确定是否所有步骤都是必要的,但现在我的应用程序完美运行:

1.- Update your web.config to support securityTokenHandlers 1.-更新您的web.config以支持securityTokenHandlers

<section name="system.identityModel" type="System.IdentityModel.Configuration.SystemIdentityModelSection, System.IdentityModel, Version=4.0.0.0, Culture=neutral, PublicKeyToken=B77A5C561934E089"/>
<section name="system.identityModel.services" type="System.IdentityModel.Services.Configuration.SystemIdentityModelServicesSection, System.IdentityModel.Services, Version=4.0.0.0, Culture=neutral, PublicKeyToken=B77A5C561934E089"/>

in the configSections node. 在configSections节点中。 And

  <securityTokenHandlers>
    <remove type="System.IdentityModel.Tokens.SessionSecurityTokenHandler,
                System.IdentityModel, Version=4.0.0.0, Culture=neutral,
                PublicKeyToken=B77A5C561934E089" />

    <add
      type="System.IdentityModel.Services.Tokens.MachineKeySessionSecurityTokenHandler,
          System.IdentityModel.Services, Version=4.0.0.0, Culture=neutral,
          PublicKeyToken=B77A5C561934E089">
      <sessionTokenRequirement lifetime="00:30:00"></sessionTokenRequirement>
    </add>
  </securityTokenHandlers>

</identityConfiguration>

as a regular node. 作为常规节点。 2.- In your Startup.Auth.cs file, update your ConfigureAuth(IAppBuilder app) like this: 2.-在您的Startup.Auth.cs文件中,更新您的ConfigureAuth(IAppBuilder应用程序),如下所示:

public void ConfigureAuth(IAppBuilder app)
        {

            UserManagerFactory = () =>
            {
                var userManager = new UserManager<SIAgroUser>(new UserStore<UserType>(new SIAgroUserDbContext()));

                IDataProtectionProvider provider = app.GetDataProtectionProvider();

                //userManager.UserTokenProvider = new Microsoft.AspNet.Identity.Owin.DataProtectorTokenProvider<UserType>(provider.Create("PasswordReset") );
                if (provider != null)
                {
                    userManager.UserTokenProvider = new DataProtectorTokenProvider<UsertType, string>(provider.Create("PasswordReset"));
                }

                return userManager;
            };

            OAuthOptions = new OAuthAuthorizationServerOptions
            {
                TokenEndpointPath = new PathString("/Token"),
                Provider = new ApplicationOAuthProvider(PublicClientId, UserManagerFactory),
                AuthorizeEndpointPath = new PathString("/api/Account/ExternalLogin"),
                AccessTokenExpireTimeSpan = TimeSpan.FromDays(14),
                AllowInsecureHttp = true
            };
            // Enable the application to use a cookie to store information for the signed in user
            // and to use a cookie to temporarily store information about a user logging in with a third party login provider
            app.UseCookieAuthentication(new CookieAuthenticationOptions());
            app.UseExternalSignInCookie(DefaultAuthenticationTypes.ExternalCookie);

            // Enable the application to use bearer tokens to authenticate users
            app.UseOAuthBearerTokens(OAuthOptions);

            // Uncomment the following lines to enable logging in with third party login providers
            //app.UseMicrosoftAccountAuthentication(
            //    clientId: "",
            //    clientSecret: "");

            //app.UseTwitterAuthentication(
            //    consumerKey: "",
            //    consumerSecret: "");

            //app.UseFacebookAuthentication(
            //    appId: "",
            //    appSecret: "");

            //app.UseGoogleAuthentication();



        }

3.- Clean up the constructor of your Startup class like this: 3.-像这样清理你的Startup类的构造函数:

static Startup()
{
   PublicClientId = "self";
}

That worked for me :) I hope it works for you too 这对我有用:)我希望它也适合你

If the host server is a virtual machine it could be exactly what the error message says. 如果主机服务器是虚拟机,则可能正是错误消息所说的内容。 Check if your Application Pool in IIS really has Load User Profile set to true like the exception says: 检查IIS中的应用程序池是否确实将“ Load User Profile设置为“true”,如下所示:

  • In the Connections pane, expand the server name, and then click Application Pools. 在“连接”窗格中,展开服务器名称,然后单击“应用程序池”。
  • Right click on you Pool 右键单击您的池
  • Advanced Settings 高级设置

在此输入图像描述

I put this one on ice for a while but was forced to come back to it. 我把这个放在冰上一段时间,但被迫回到它。 I found the solution here: Generating reset password token does not work in Azure Website 我在这里找到了解决方案: 生成重置密码令牌在Azure网站中不起作用

This error happens for me on a shared hosting provider, at the line: 我在共享主机提供商处发生此错误,该行:

var provider = new DpapiDataProtectionProvider("SITENAME");

The solution was quite simple. 解决方案非常简单。 First change the above line to this: 首先将上面的行更改为:

var provider = new MachineKeyProtectionProvider();

Then create a new file, which I have in my Utilities namespace, like so: 然后在我的Utilities命名空间中创建一个新文件,如下所示:

using Microsoft.Owin.Security.DataProtection;
using System.Web.Security;

namespace <yournamespace>.Utilities
{
    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);
        }
    }
}

Et voila! 瞧瞧! Problem solved. 问题解决了。 Just remember, in your password reset controller method, you will also have to use this provider, otherwise you will get an Invalid Token error. 请记住,在密码重置控制器方法中,您还必须使用此提供程序,否则您将收到Invalid Token错误。

Getting the UserManager from the Owin Pipeline, as its set in App_Start/Startup.Auth.cs, works on Azure. 从Owin管道获取UserManager,就像它在App_Start / Startup.Auth.cs中的设置一样,适用于Azure。 I'm unsure as to how this works specifically. 我不确定这是如何具体的。 The DpApi should work in Azure with the solution described in the first link. DpApi应该在Azure中使用第一个链接中描述的解决方案。

If the DpApi has a static machine key set in Web.config all server machines will be able to decrypt the encrypted data created by another machine in your webfarm is the understanding behind this. 如果DpApi在Web.config中设置了静态机器密钥,则所有服务器机器都能够解密由webfarm中的另一台机器创建的加密数据。

(code as given in the standard template - from AccountController.cs) (标准模板中给出的代码 - 来自AccountController.cs)

 private UserManager userManager;
    public UserManager UserManager
    {
        get { return userManager ?? HttpContext.GetOwinContext().GetUserManager<UserManager>(); }
        private set { userManager = value; }
    }

After me and two other people have messing with this error for dayS we discovered something intresting in the IIS. If the Load User Profile is switched following is created in applicationhost.config在我和其他两个人弄乱了 dayS 的这个错误之后,我们在 IIS 中发现了一些有趣的东西。如果切换了加载用户配置文件,则会在 applicationhost.config 中创建以下内容

loadUserProfile="true"

but when you turn it off it also works, but now the line但是当你关闭它时它也可以工作,但是现在这条线

loadUserProfile="false"

has been added.已添加。 So difference was that default value had to be written in applicationhost.config to make it work所以区别在于必须在 applicationhost.config 中写入默认值才能使其工作

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM