ASP.NET Core 2.1 Cookie身份验证似乎具有服务器相​​似性 [英] ASP.NET Core 2.1 cookie authentication appears to have server affinity

查看:79
本文介绍了ASP.NET Core 2.1 Cookie身份验证似乎具有服务器相​​似性的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

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

I'm developing an application in ASP.NET Core 2.1, and running it on a Kubernetes cluster. I've implemented authentication using OpenIDConnect, using Auth0 as my provider.

一切正常.标有[Authorize]属性的操作或控制器将匿名用户重定向到身份提供者,他们登录,重定向回来,而Bob是您的叔叔.

This all works fine. Actions or controllers marked with the [Authorize] attribute redirect anonymous user to the identity provider, they log in, redirects back, and Bob's your uncle.

当我将部署扩展到2个或更多容器时,就会开始出现问题.当用户访问应用程序时,他们将登录,并且根据回调过程中为它们提供服务的容器,身份验证是成功还是失败.即使在身份验证成功的情况下,当用户点击未授权的容器时,反复进行F5-ing操作仍将最终重定向到身份提供者.

The problems start occurring when I scale my deployment to 2 or more containers. When a user visits the application, they log in, and depending on what container they get served during the callback, authentication either succeeds or fails. Even in the case of authentication succeeding, repeatedly F5-ing will eventually redirect to the identity provider when the user hits a container they aren't authorized on.

在这方面,我的思路是,用户使用cookie身份验证将cookie存储在浏览器中,并随每个请求一起传递,应用程序对其进行解码并获取JWT,然后从中获取声明,并且用户已通过身份验证.这使整个事情变得无状态,因此无论为请求提供服务的容器如何,它都应该工作.但是,如上所述,它似乎并没有真正以这种方式工作.

My train of thought on this would be that, using cookie authentication, the user stores a cookie in their browser, that gets passed along with each request, the application decodes it and grabs the JWT, and subsequently the claims from it, and the user is authenticated. This makes the whole thing stateless, and therefore should work regardless of the container servicing the request. As described above however, it doesn't appear to actually work that way.

我在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);
            }
        };
    });

我花了数小时试图解决这个问题,然后空了出来.从理论上讲,我现在唯一可以想到的就是使用粘性负载平衡,但这要比实际解决问题更多地应用创可贴.

I've spent hours trying to address this issue, and came up empty. The only thing I can think of that could theoretically work now is using sticky load balancing, but that's more applying a band-aid than actually fixing the problem.

使用Kubernetes的主要原因之一是其弹性和很好地处理扩展的能力.就目前而言,我只能扩展我的支持服务,而我的主应用程序必须作为一个单独的Pod运行.这远非理想.

One of the main reasons to use Kubernetes is its resilience and ability to handle scaling very well. As it stands, I can only scale my backing services, and my main application would have to run as a single pod. That's far from ideal.

也许某个地方有某种机制可以与我不知道的特定实例建立亲和力?

Perhaps there is some mechanism somewhere that creates affinity with a specific instance that I'm not aware of?

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

I hope someone can point me in the right direction.

谢谢!

推荐答案

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

The cookie issued by authentication is encrypted via Data Protection. Data Protection by default is scoped to a particular application, or instance thereof. If you need to share an auth cookie between instances, you need to ensure that the data protection keys are persisted to a common location and that the application name is the same.

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

您可以在文档.

这篇关于ASP.NET Core 2.1 Cookie身份验证似乎具有服务器相​​似性的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆