交换 Azure Web App 部署槽会注销 ASP.NET Core RC2 中的所有用户
Posted
技术标签:
【中文标题】交换 Azure Web App 部署槽会注销 ASP.NET Core RC2 中的所有用户【英文标题】:Swapping Azure Web App deployment slots logs out all users in ASP.NET Core RC2 【发布时间】:2016-10-17 10:01:09 【问题描述】:每当我更新作为 Azure Web 应用程序运行的 ASP.NET Core RC2 网站时,它都会注销所有用户。它似乎与将登台部署槽交换到生产环境有关(我使用从 VS 到登台的 Web 部署,并将其设置为自动交换到生产环境)。如果我直接更新生产槽就可以了,但我不想那样做。我不知道如何配置它,不胜感激!
这是我现在的配置方式,我的网站只允许直接登录(没有 Facebook 登录等):
在启动中的 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();
并在启动时配置
app.UseIdentity();
【问题讨论】:
您的暂存槽和生产槽使用相同的数据库吗?登录会话存储在数据库中,因此如果您将 2 个数据库用于 2 个插槽,并且在交换时不保留这些数据库设置,那么这些登录会话也会被交换。 两个插槽都使用同一个数据库,但我认为这与这种情况无关......我们的网站不使用任何会话信息或将任何会话信息保存到数据库中。发生的事情可能是登录cookie在交换时变得无效......我的猜测是某些东西导致cookie在不同的插槽上以不同的方式加密...... 更详细一点:当我交换部署槽时,登录 cookie 仍然存在于浏览器中......网站只是认为它们无效并忽略它们。 【参考方案1】:经过大量研究......我认为我有这个工作。
因此,对于任何想要一个使用 Identity 登录的 ASP.NET Core RC2 网站,并希望将其托管在 Azure Web 应用程序上,并希望使用部署槽通过交换进行更新的任何人,以及 不希望每次网站更新时每个用户都退出...继续阅读!
** 通常,Azure 会为您提供一些神奇的默认配置,使单个 Web 应用程序中的所有实例协同工作。部署槽的问题在于它本质上就像两个完全独立的 Web 应用程序,所以所有的魔法都消失了。
您需要正确配置数据保护才能使其正常工作。这有点令人困惑,因为 .NET Core Identity 的文档没有明确提及依赖或要求您正确配置数据保护,但确实如此。数据保护是它在后台使用来加密应用程序登录 cookie。
ConfigureServices中需要如下代码:
services.AddDataProtection()
.SetApplicationName("myweb")
.ProtectKeysWithCertificate("thumbprint");
services.AddSingleton<IXmlRepository, CustomDataProtectionRepository>();
每一段的解释:
-
设置应用程序名称可让您在使用相同应用程序名称的多个应用程序之间共享受保护的数据。可能并非所有情况都需要,但对我们的情况没有影响。
您需要使用跨两个部署槽一致的自定义密钥加密方法。默认值特定于每个部署槽,并且只能在该槽内使用。如果您查看key encryption at rest,Azure 默认使用 Windows DPAPI。不会为我们的目的工作。所以我选择使用证书,只需输入 Azure 门户中看到的指纹即可。注意:对于那些讨厌证书及其周围所有行话的人,.NET Core 文档说您需要一个支持 CAPI 私钥的 X.509 证书,否则它将不起作用。 blah blah blah blah 使用您为您的网站获得的 SSL 证书,它应该可以正常工作。
顺便说一句:您必须做一些额外的谷歌搜索才能真正使用证书。在 Azure 的情况下,数据保护文档会让您束手无策……仅使用上面的代码,当您部署到 Azure 时,您可能会收到 500 错误。首先,确保您已将证书上传到 Web 应用程序的“自定义域和 SSL”部分。其次,您需要添加 WEBSITE_LOAD_CERTIFICATES 应用程序设置并将您的证书的指纹添加到其中以便使用它。见using certificates in azure websites。
一旦你设置了一个证书来加密数据......它会破坏关于存储数据位置的任何默认配置——Azure 将它存储在一个共享文件夹中,默认情况下你的所有实例都可以访问(此处描述的默认值 @ 987654323@)。但是不同的部署槽是分开的......所以内置的文件系统和注册表选项没有帮助。您必须按照此处所述编写自定义实现:key storage providers。但是哦,等等……自定义密钥存储库底部的部分是一个 1-liner,没有链接或关于如何连接它的解释……你真的需要在这里阅读:key management,转到 IXmlRepository 部分.不幸的是,IDataProtectionBuilder 对所有内容都有方便的扩展,除了您需要在此处执行的操作,因此我们向服务提供商注册此自定义 IXmlRepository 的行。尽管该接口的通用名称令人震惊,但它只会影响数据保护,不会影响您的其他内容。
未显示的是 CustomDataProtectionRepository 的实现。我使用了 Azure Blob 存储。这是一个非常简单的界面,如果您需要帮助,请发表评论。
OMG 终于让它工作了。享受丢失密码的客户服务请求减少 500% ;)
【讨论】:
这是否适用于当前尚未迁移到 Core 的 ASP.NET 5 应用程序? 这只是真正适用于asp.net core。 我只是想让你知道微软有一个 (alpha) 官方 azure 存储扩展 nuget.org/packages/… 您是否知道是否有任何理由不能在第 3 步中使用自签名证书(或 azurewebsites.net 子域上的 azure 默认 https 证书)和 WEBSITE_LOAD_CERTIFICATES环境?我只是无法通过我的开发网站抛出一个启动错误,说“找不到带有指纹 'xxx' 的证书”,尽管这在本地对 IIS Express 及其证书非常有效。我希望在转向生产并使用我真正的 SSL 证书之前在开发网站上对其进行测试,但此时我几乎没有想法 嘿@Yellowfive,为此欢呼-只是想知道您是否知道SSL证书过期/更新时会发生什么-此时所有现有的身份验证cookie都会失效吗?【参考方案2】:我尝试将许多文章(包括此处的文章)拼凑成一个完整的解决方案。这是我想出的。原博文: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($"storageUrlsasToken");
//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);
如果以这种方式存储,这里是一个示例 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"
【讨论】:
由于生成用户会话的密钥相当敏感,作为额外的安全层来缓解 blob 存储泄露,我建议使用ProtectKeysWithAzureKeyVault
,如:services.AddDataProtection().PersistKeysToAzureBlobStorage(...).ProtectKeysWithAzureKeyVault(...)
【参考方案3】:
我更新了 EricksonG 的答案以包含一些新库。这样就解决了问题,实现起来也很快。
您将需要这些软件包:
Microsoft.AspNetCore.DataProtection
Azure.Extensions.AspNetCore.DataProtection.Blobs
Azure.Extensions.AspNetCore.DataProtection.Keys
这是我的代码版本。
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);
您可能需要确保容器存在,但这肯定会自动处理创建 blob 和密钥。您甚至不必看到它们。
另外:文档建议这应该在其他任何可能进行身份验证的操作之前进入您的配置,因为这些工具有时对数据保护有自己的看法。
应用设置:
"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.
保护:照原样,这并不是那么安全。让它工作,然后如果您需要更高的安全性,请通过 .ProtectKeysWithAzureKeyVault() 或其他扩展方法之一进行 Key Vault 保护,从而进一步实现这一点。当然,使用 KeyVault 作为连接字符串也是一个好主意。
应用程序名称: .SetApplicationName() 行是可选的;它有助于在应用程序之间隔离或共享数据。来自文档:默认情况下,数据保护系统会根据应用的内容根路径将应用彼此隔离,即使它们共享同一个物理密钥存储库。这会阻止应用了解彼此的受保护负载。
更多:https://docs.microsoft.com/en-us/aspnet/core/security/data-protection/configuration/overview?view=aspnetcore-5.0
【讨论】:
我实现了这一点,但每当我将 staging 换成 prod 时,我仍然会被注销。它需要去的中间件订单中的某个地方吗?我把它放在 adddbcontext 和 adddefaultidentity 服务之前的顶部附近。我的应用程序是 .Net Core 3.1,以防万一。 @EnergyCJ 当然,请确保您已将其部署到两个插槽。我会尝试尽早将它放在那里,看看是否会有所作为。 我不得不手动添加容器;否则抛出异常,表明它没有找到。 Blob 和键是按说明自动创建的。【参考方案4】:谢谢布赖恩。我现在有这个工作。我认为它需要这个和 web 配置文件中的机器密钥。我说我认为是因为在我交换插槽然后单击浏览按钮后,我仍然看到我的注册和登录按钮而不是个人资料信息,但是一旦我导航到另一个页面,我就会看到个人资料信息。感谢您的回复。
【讨论】:
以上是关于交换 Azure Web App 部署槽会注销 ASP.NET Core RC2 中的所有用户的主要内容,如果未能解决你的问题,请参考以下文章
Windows Azure Web Site (15) 基于Azure Web App的企业官网改造
我应该使用 Docker 将 Web API 项目(微服务)部署到 Azure Web App [关闭]
在 Azure 上部署 Asp.NET Core Web App