簡體   English   中英

ASP.NET Core 2.1 cookie身份驗證似乎具有服務器關聯

[英]ASP.NET Core 2.1 cookie authentication appears to have server affinity

我正在ASP.NET Core 2.1中開發一個應用程序,並在Kubernetes集群上運行它。 我使用OpenIDConnect實現了身份驗證,使用Auth0作為我的提供者。

一切正常。 標有[Authorize]屬性的操作或控制器將匿名用戶重定向到身份提供者,他們登錄,重定向回來,Bob是你的叔叔。

當我將部署擴展到2個或更多容器時,問題就開始出現了。 當用戶訪問應用程序時,他們會登錄,並且根據回調期間服務的容器,身份驗證成功或失敗。 即使在身份驗證成功的情況下,當用戶點擊未經授權的容器時,重復F5-ing最終也會重定向到身份提供者。

我對此的想法是,使用cookie身份驗證,用戶將cookie存儲在他們的瀏覽器中,隨每個請求一起傳遞,應用程序對其進行解碼並抓取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通過Data Protection加密。 默認情況下,數據保護的范圍限定為特定應用程序或其實例。 如果需要在實例之間共享auth cookie,則需要確保將數據保護密鑰持久保存到公共位置,並確保應用程序名稱相同。

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

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

每當我重新啟動Azure App Service(PaaS)並且我的用戶的cookie不再有效時,我就遇到了同樣的問題。 我的應用程序使用ASP.NET Core Identity框架。

以下文檔說明了將數據保護配置為跨多個應用程序實例甚至多個Web應用程序的各種方法:

https://docs.microsoft.com/en-us/aspnet/core/security/data-protection/configuration/overview

我發現使用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");

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM