我正在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");
你可以在docs找到更多信息。
每当我重新启动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");