简体   繁体   English

交换 Azure Web App 部署槽会注销 ASP.NET Core RC2 中的所有用户

[英]Swapping Azure Web App deployment slots logs out all users in ASP.NET Core RC2

Whenever I updated my ASP.NET Core RC2 website running on as an Azure Web App, it logs out all users.每当我更新作为 Azure Web 应用程序运行的 ASP.NET Core RC2 网站时,它都会注销所有用户。 It seems to be related to swapping a staging deployment slot to production (I use web deploy from VS to staging, and have it set to auto-swap to production).这似乎与将暂存部署槽交换到生产有关(我使用从 VS 到暂存的 web 部署,并将其设置为自动交换到生产)。 If I do a direct update of the production slot it's fine, but I don't want to do that.如果我直接更新生产槽,那很好,但我不想这样做。 I am at a loss as to how to configure this, help would be appreciated!我不知道如何配置它,将不胜感激!

Here is how I have it configured right now, my site only allows logging in directly (no facebook login etc.):这是我现在配置它的方式,我的网站只允许直接登录(没有 Facebook 登录等):

In ConfigureServices in Startup在启动中的 ConfigureServices 中

// found some post that said this would help... doesn't seem to work...
services.AddDataProtection()
        .SetApplicationName("myweb");

services.AddIdentity<MyUser, MyRole>(options =>
{
    options.Cookies.ApplicationCookie.CookieDomain = settings.CookieDomain; // cookie domain lets us share cookies across subdomains
    options.Cookies.ApplicationCookie.LoginPath = new PathString("/account/login");
    options.Cookies.ApplicationCookie.ReturnUrlParameter = "ret";
    options.Cookies.ApplicationCookie.CookieSecure = CookieSecureOption.Never; // TODO: revisit site-wide https

    // allow login cookies to last for 30 days from last use
    options.Cookies.ApplicationCookie.ExpireTimeSpan = TimeSpan.FromDays(60);
    options.Cookies.ApplicationCookie.SlidingExpiration = true;

    // I think this needs to at least be longer than cookie expiration to prevent security stamp from becoming invalid before the cookie?
    options.SecurityStampValidationInterval = TimeSpan.FromDays(90);
})
.AddUserStore<MyUserStore>() // custom stores to hook up our old databases to new identity system
.AddRoleStore<MyRoleStore>()
.AddDefaultTokenProviders();

And in Configure in Startup并在启动时配置

app.UseIdentity();

After much research... I think that I have this working. 经过大量研究……我认为我已经在工作。

So for anyone who wants an ASP.NET Core RC2 website that uses the Identity stuff for login, and wants to host it on an Azure Web App, and wants to use the Deployment Slots to do updates via swapping, and doesn't want every user to get logged out every time the website is updated... read on! 因此,对于任何想要使用身份信息进行登录的ASP.NET Core RC2网站,并希望将其托管在Azure Web应用程序上,并且想要使用部署插槽通过交换进行更新的人, 并不想让所有人用户在每次更新网站时都会注销...请继续阅读!

** Usually, Azure gives you some magical default configuration that makes all of the instances in a single Web App work together. **通常,Azure为您提供了一些神奇的默认配置,使单个Web App中的所有实例可以协同工作。 The issue with deployment slots is that it essentially acts like two completely separate Web Apps, so all the magic is gone. 部署插槽的问题在于,它实际上就像两个完全独立的Web Apps一样,因此所有魔力都消失了。

You need to configure Data Protection correctly to make this work. 您需要正确配置Data Protection才能执行此操作。 It is a bit confusing because the documentation for .NET Core Identity makes no explicit mention of depending on or requiring that you configure Data Protection correctly, but it does. 这有点令人困惑,因为.NET Core Identity文档没有明确提及依赖或要求您正确配置Data Protection的情况,但是确实如此。 Data Protection is what it uses under the hood to encrypt the application login cookie. 数据保护功能是它在后台用来加密应用程序登录cookie的功能。

The following code is needed in ConfigureServices: ConfigureServices中需要以下代码:

services.AddDataProtection()
    .SetApplicationName("myweb")
    .ProtectKeysWithCertificate("thumbprint");

services.AddSingleton<IXmlRepository, CustomDataProtectionRepository>();

Explanation of each piece: 每件的说明:

  1. Setting the application name lets you share the protected data across multiple applications that use this same application name. 设置应用程序名称可让您在使用同一应用程序名称的多个应用程序之间共享受保护的数据。 May not be required for all scenarios, but doesn't hurt for ours. 可能并非所有情况都需要,但对我们而言并不有害。
  2. You need to use a custom key encryption method that is consistent across both deployment slots. 您需要使用在两个部署槽之间都一致的自定义密钥加密方法。 The default is specific to each deployment slot and can only be used within that slot. 默认值特定于每个部署插槽,并且只能在该插槽中使用。 If you look at key encryption at rest , Azure uses Windows DPAPI by default. 如果您看静态的密钥加密,则默认情况下,Azure使用Windows DPAPI。 Not gonna work for our purposes. 不会为我们的目的工作。 So I chose to use a certificate, just enter the thumbprint as seen in the Azure portal. 因此,我选择使用证书,只需输入在Azure门户中看到的指纹即可。 NOTE: for those who hate certificates and all the jargon around it, the .NET Core documentation says you need a X.509 certificate that supports CAPI private keys or it won't work. 注意:对于那些讨厌证书及其周围所有术语的人,.NET Core文档说您需要支持CAPI私钥的X.509证书,否则它将无法正常工作。 blah blah blah blah use the SSL certificate you got for your website, it should work just fine. 等等等等使用您为您的网站获得的SSL证书,它应该可以正常工作。
  3. An aside: you have to do some extra googling to actually make using the certificate work. 顺便说一句:您必须做一些额外的搜索才能真正使用证书。 The Data Protection documentation kind of leaves you hanging in the case of Azure... just using the code above, you will likely get a 500 error when you deploy to Azure. 数据保护文档让您陷入Azure的困扰...仅使用上面的代码,当部署到Azure时,您可能会遇到500错误。 Firstly, make sure you have uploaded your certificate in the "Custom domains and SSL" section of your Web App. 首先,请确保您已将证书上载到Web应用程序的“自定义域和SSL”部分。 Secondly, you need to add the WEBSITE_LOAD_CERTIFICATES Application Setting and add the thumbprint of your certificate to that in order to use it. 其次,您需要添加WEBSITE_LOAD_CERTIFICATES应用程序设置并将证书的指纹添加到该应用程序才能使用它。 See using certificates in azure websites . 请参阅在Azure网站上使用证书
  4. Once you set a certificate to encrypt the data... it blows away any default configuration about where to store the data -- Azure stores it in a shared folder that all of your instances can access by default (defaults described here data protection defaults ). 设置用于加密数据的证书后,它会破坏关于存储数据位置的任何默认配置-Azure将其存储在您的所有实例都可以默认访问的共享文件夹中(默认值此处介绍了数据保护默认值 ) 。 But different deployment slots are separate... so the built-in file system and registry options are no help. 但是不同的部署插槽是分开的...因此内置文件系统和注册表选项无济于事。 You have to write a custom implementation as described here: key storage providers . 您必须编写一个自定义实现,如下所述: 密钥存储提供程序 But oh wait... the section at the bottom on custom key repositories is a 1-liner with no link or explanation about how to hook it up... you really need to read here: key management , go to the IXmlRepository section. 但是,等等,自定义密钥存储库底部的部分是1-liner,没有链接或有关如何将其连接的说明...您真的需要在这里阅读: 密钥管理 ,转到IXmlRepository部分。 Unfortunately the IDataProtectionBuilder has handy extensions for everything except what you need to do here, thus the line where we register this custom IXmlRepository with the service provider. 不幸的是,除了您需要在此处执行的操作之外,IDataProtectionBuilder拥有适用于所有内容的便捷扩展,因此,我们向服务提供商注册了此自定义IXmlRepository的行。 Despite the alarmingly generic name of that interface, it only impacts Data Protection and won't mess with your other stuff. 尽管该接口的名称令人震惊,但它只会影响数据保护,不会与您的其他内容混淆。
  5. Not shown is the implementation of CustomDataProtectionRepository. 未显示CustomDataProtectionRepository的实现。 I used Azure blob storage. 我使用了Azure Blob存储。 It is a pretty simple interface, make a comment if you need help with that though. 这是一个非常简单的界面,如果需要帮助,请发表评论。

And OMG finally we have it working. 最终,OMG我们开始运作了。 Enjoy the 500% decrease in lost password customer service requests ;) 享受减少500%的密码丢失客户服务请求;)

I tried to piece together a number of articles include the one here into a complete solution. 我试图整理一些文章,其中包括一个完整的解决方案。 Here is what I came up with. 这是我想出的。 Original blog post: http://intellitect.com/staying-logged-across-azure-app-service-swap/ 原始博客文章: http : //intellitect.com/staying-logged-across-azure-app-service-swap/

// Add Data Protection so that cookies don't get invalidated when swapping slots.
string storageUrl = Configuration.GetValue<string>("DataProtection:StorageUrl");
string sasToken = Configuration.GetValue<string>("DataProtection:SasToken");
string containerName = Configuration.GetValue<string>("DataProtection:ContainerName");
string applicationName = Configuration.GetValue<string>("DataProtection:ApplicationName");
string blobName = Configuration.GetValue<string>("DataProtection:BlobName");

// If we have values for all these things set up the data protection store in Azure.
if (storageUrl != null && sasToken != null && containerName != null && applicationName != null && blobName != null)
{
    // Create the new Storage URI
    Uri storageUri = new Uri($"{storageUrl}{sasToken}");

    //Create the blob client object.
    CloudBlobClient blobClient = new CloudBlobClient(storageUri);

    //Get a reference to a container to use for the sample code, and create it if it does not exist.
    CloudBlobContainer container = blobClient.GetContainerReference(containerName);
    container.CreateIfNotExists();

    services.AddDataProtection()
        .SetApplicationName(applicationName)
        .PersistKeysToAzureBlobStorage(container, blobName);
}

Here is a sample appsettings.json if they are stored that way. 如果以这种方式存储,这是一个示例appsettings.json。

{
  "DataProtection": {
    "ApplicationName": "AppName",
    "StorageUrl": "https://BlobName.blob.core.windows.net",
    "SasToken": "?sv=YYYY-MM-DD&ss=x&srt=xxx&sp=xxxxxx&se=YYYY-MM-DDTHH:MM:SSZ&st=YYYY-MM-DDTHH:MM:SSZ&sip=a.b.c.d-w.x.y.z&spr=https&sig=xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx",
    "ContainerName": "data-protection-container-name", // All lower case with dashes and numbers.
    "BlobName": "data-protection-blob-name"
  }
}

I updated EricksonG's answer to include some new libraries.我更新了 EricksonG 的答案以包含一些新库。 This solves the problem and it's quick to implement.这解决了问题,而且实施起来很快。

You will need these packages:您将需要这些软件包:

 Microsoft.AspNetCore.DataProtection
 Azure.Extensions.AspNetCore.DataProtection.Blobs
 Azure.Extensions.AspNetCore.DataProtection.Keys

Here's my version of the code.这是我的代码版本。

var connectionString = configuration["DataProtection:ConnectionString"];
var containerName = configuration["DataProtection:ContainerName"];
var applicationName = configuration["DataProtection:ApplicationName"];
var blobName = configuration["DataProtection:BlobName"];

services.AddDataProtection()
        .SetApplicationName(applicationName)  // This is optional. See below.
        .PersistKeysToAzureBlobStorage(connectionString, containerName, blobName);

You might need to make sure the container exists, however this will definitely handle creating blob and the keys automatically.您可能需要确保容器存在,但这肯定会自动处理创建 blob 和键。 You never even have to see them.你甚至不必见他们。

Also: docs suggests this should go in your config before anything else that might be doing auth, because those tools sometimes have their own take on Data Protection.另外:docs 建议这应该在您的配置中先于其他任何可能进行身份验证的操作,因为这些工具有时对数据保护有自己的看法。

AppSettings:应用设置:

{
  "DataProtection": {
    "ApplicationName": "AppName",  // Can technically be anything. See below for details on this.
    "ConnectionString": "<Your key from azure>",
    "ContainerName": "data-protection", // All lower case with dashes and numbers. There's no need to change this, but you can.
    "BlobName": "data-protection-keys" // Same. 
  }
}

Protection: As-is, this is not all that secure.保护:按原样,这并不是那么安全。 Get it working, and then if you need more security, take this further by doing Key Vault protection via .ProtectKeysWithAzureKeyVault(), or one of the other extension methods.让它正常工作,然后如果您需要更多安全性,请通过 .ProtectKeysWithAzureKeyVault() 或其他扩展方法之一执行 Key Vault 保护来进一步提高安全性。 It would also be a good idea to use KeyVault for the connection string, of course.当然,将 KeyVault 用于连接字符串也是一个好主意。

Application Name: The .SetApplicationName() line is optional;应用程序名称: .SetApplicationName() 行是可选的; it helps isolate or share data between apps.它有助于在应用程序之间隔离或共享数据。 From the docs: by default, the Data Protection system isolates apps from one another based on their content root paths, even if they're sharing the same physical key repository.来自文档:默认情况下,数据保护系统根据应用程序的内容根路径将应用程序彼此隔离,即使它们共享相同的物理密钥存储库。 This prevents the apps from understanding each other's protected payloads.这会阻止应用程序了解彼此的受保护负载。

More: https://docs.microsoft.com/en-us/aspnet/core/security/data-protection/configuration/overview?view=aspnetcore-5.0更多: https : //docs.microsoft.com/en-us/aspnet/core/security/data-protection/configuration/overview?view=aspnetcore-5.0

Thanks Brian.谢谢布莱恩。 I have this working now.我现在有这个工作。 I think it required both this and the machine key in the web configuration file.我认为它需要这个和 web 配置文件中的机器密钥。 I say I think because after I swap slots and then click the browse button I still see my register and login buttons instead of the profile info but as soon as I navigate to another page I see the profile info.我说我认为是因为在我交换插槽然后单击浏览按钮后,我仍然看到我的注册和登录按钮而不是个人资料信息,但是一旦我导航到另一个页面,我就会看到个人资料信息。 I appreciate the response.我很欣赏回应。

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

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