C# ASP.NET Core 2.1 cookie身份验证似乎具有服务器关联性

C# ASP.NET Core 2.1 cookie身份验证似乎具有服务器关联性,c#,asp.net-core,kubernetes,openid-connect,auth0,C#,Asp.net Core,Kubernetes,Openid Connect,Auth0,我正在ASP.NETCore2.1中开发一个应用程序,并在Kubernetes集群上运行它。我已经使用OpenIDConnect实现了身份验证,使用Auth0作为我的提供者 这一切都很好。标记有[Authorize]属性的操作或控制器将匿名用户重定向到身份提供程序,它们将登录并重定向回,Bob是您的叔叔 当我将部署扩展到2个或更多容器时,问题就开始出现了。当用户访问应用程序时,他们会登录,根据回调过程中所使用的容器,身份验证会成功或失败。即使在身份验证成功的情况下,当用户点击未经授权的容器时,重

我正在ASP.NETCore2.1中开发一个应用程序,并在Kubernetes集群上运行它。我已经使用OpenIDConnect实现了身份验证,使用Auth0作为我的提供者

这一切都很好。标记有
[Authorize]
属性的操作或控制器将匿名用户重定向到身份提供程序,它们将登录并重定向回,Bob是您的叔叔

当我将部署扩展到2个或更多容器时,问题就开始出现了。当用户访问应用程序时,他们会登录,根据回调过程中所使用的容器,身份验证会成功或失败。即使在身份验证成功的情况下,当用户点击未经授权的容器时,重复F5-ing最终也会重定向到身份提供程序

我的思路是,使用cookie身份验证,用户在浏览器中存储一个cookie,该cookie随每个请求一起传递,应用程序对其进行解码并获取JWT,随后从JWT获取声明,然后对用户进行身份验证。这使得整个过程成为无状态的,因此无论为请求提供服务的容器是什么,都应该可以工作。然而,如上所述,它似乎并没有以这种方式实际工作

我在
Startup.cs
中的配置如下所示:

services.AddAuthentication(options =>
    {
        options.DefaultAuthenticateScheme = CookieAuthenticationDefaults.AuthenticationScheme;
        options.DefaultSignInScheme = CookieAuthenticationDefaults.AuthenticationScheme;
        options.DefaultChallengeScheme = CookieAuthenticationDefaults.AuthenticationScheme;
    })
    .AddCookie()
    .AddOpenIdConnect("Auth0", options =>
    {
        options.Authority = $"https://{Configuration["Auth0:Domain"]}";

        options.ClientId = Configuration["Auth0:ClientId"];
        options.ClientSecret = Configuration["Auth0:ClientSecret"];

        options.ResponseType = "code";

        options.Scope.Clear();
        options.Scope.Add("openid");
        options.Scope.Add("profile");
        options.Scope.Add("email");

        options.TokenValidationParameters = new TokenValidationParameters
        {
            NameClaimType = "name"
        };

        options.SaveTokens = true;

        options.CallbackPath = new PathString("/signin-auth0");

        options.ClaimsIssuer = "Auth0";

        options.Events = new OpenIdConnectEvents
        {
            OnRedirectToIdentityProviderForSignOut = context =>
            {
                var logoutUri =
                    $"https://{Configuration["Auth0:Domain"]}/v2/logout?client_id={Configuration["Auth0:ClientId"]}";

                var postLogoutUri = context.Properties.RedirectUri;
                if (!string.IsNullOrEmpty(postLogoutUri))
                {
                    if (postLogoutUri.StartsWith("/"))
                    {
                        var request = context.Request;
                        postLogoutUri = request.Scheme + "://" + request.Host + request.PathBase +
                                        postLogoutUri;
                    }

                    logoutUri += $"&returnTo={Uri.EscapeDataString(postLogoutUri)}";
                }

                context.Response.Redirect(logoutUri);
                context.HandleResponse();

                return Task.CompletedTask;
            },
            OnRedirectToIdentityProvider = context =>
            {
                context.ProtocolMessage.SetParameter("audience", "https://api.myapp.com");

                // Force the scheme to be HTTPS, otherwise we end up redirecting back to HTTP in production.
                // They should seriously make it easier to make Kestrel serve over TLS in the same way ngninx does...
                context.ProtocolMessage.RedirectUri = context.ProtocolMessage.RedirectUri.Replace("http://",
                    "https://", StringComparison.OrdinalIgnoreCase);

                Debug.WriteLine($"RedirectURI: {context.ProtocolMessage.RedirectUri}");

                return Task.FromResult(0);
            }
        };
    });
我花了几个小时试图解决这个问题,结果一无所获。我现在唯一能想到的理论上是使用粘性负载平衡,但这比实际解决问题更能应用创可贴。 使用Kubernetes的主要原因之一是它的弹性和很好地处理缩放的能力。目前,我只能扩展我的支持服务,我的主应用程序只能作为一个单独的pod运行。那远非理想

也许某个地方有某种机制可以创建与我不知道的特定实例的关联

我希望有人能给我指出正确的方向


谢谢

通过数据保护对身份验证发布的cookie进行加密。默认情况下,数据保护的范围是特定的应用程序或其实例。如果需要在实例之间共享身份验证cookie,则需要确保数据保护密钥持久化到公共位置,并且应用程序名称相同

services.AddDataProtection()
    .PersistKeysToFileSystem(new DirectoryInfo(@"\\server\share\directory\"))
    .SetApplicationName("MyApp");

您可以在中找到更多信息。

每当我重新启动Azure应用程序服务(PaaS)时,我都会遇到相同的问题,并且我的用户cookie不再有效。我的应用程序使用ASP.NET核心身份框架

以下文档解释了将数据保护配置为跨多个应用程序实例甚至多个web应用程序的各种方法:

我发现使用blob存储帐户是使其工作的最快方法:

var storageAccount = CloudStorageAccount.Parse(configuration["Configuration key to Azure storage connection string"]);
var client = storageAccount.CreateCloudBlobClient();
var container = client.GetContainerReference("key-container");

container.CreateIfNotExistsAsync().GetAwaiter().GetResult();

services.AddDataProtection()
    .SetApplicationName("Application Name")
    .PersistKeysToAzureBlobStorage(container, "keys.xml");

clientkey可能是密钥吗?它如何保存令牌?在服务器上,还是在数据库中?你绝对是救命恩人!我将其设置为在Azure Blob存储中持久化。考虑到密钥所在的容器是私有的,SAS是保密的,是否需要加密密钥?对我来说,这似乎是多余的,因为密钥在传输中是通过HTTPS加密的,而唯一的其他方法是访问Azure门户,在这一点上加密也没有帮助?始终在静止时加密密钥。我记不起现在是哪家公司了,但新闻中只报道了一家公司泄露了他们的存储空间。永远不要认为储存是安全的。AVEITAS有一个你可能会考虑的额外选择。Kubernetes可以配置为在每个客户端的基础上自动处理用户的会话关联。本质上,这将导致根据您选择的条件将给定用户发送到容器的同一实例。@MatthewBrubaker:这称为“粘性会话”,它们是一种反模式。它在一定程度上否定了拥有多个实例的好处,并完全扼杀了故障转移。@ChrisPratt我同意粘性会话是一种代码气味(这一点非常糟糕),但我不会把它们称为完全反模式。通过允许它们,您还隐式地允许存在内部状态的可能性。它确实要求您更加勤奋地确保不允许其他内部状态存在,但如果需要,可以对其进行管理。