.net - /signin-oidc 多个客户端实例上的 404(数据保护问题)
问题描述
问题
我正在开发一个 .NET Framework 4.7.2 MVC 应用程序,它总是在负载均衡器后面并且工作得很好,除非我添加 MVC 应用程序的第二个实例。
IdentityServer4 本身正在使用数据保护,但非核心客户端存在此问题。
我找不到 .NET 框架的数据保护等效项。有没有,或者我看到了什么不同的东西?
如果我从负载均衡器中删除一个实例,它会再次工作。
最小的工作示例
app.UseCookieAuthentication(new CookieAuthenticationOptions
{
AuthenticationType = CookieAuthenticationDefaults.AuthenticationType,
SlidingExpiration = true
});
app.UseOpenIdConnectAuthentication(new OpenIdConnectAuthenticationOptions
{
ClientId = clientId,
Authority = identityServerUrl,
//Get this dynamically, register all possible values in IDS.
//https://github.com/IdentityServer/IdentityServer3/issues/1458
RedirectUri = redirectUrl,
PostLogoutRedirectUri = postLogoutRedirectUrl,
ResponseType = "code id_token",
Scope = scope,
SaveTokens = true,
TokenValidationParameters = new TokenValidationParameters
{
NameClaimType = "name",
RoleClaimType = "role"
},
RequireHttpsMetadata = false,
SignInAsAuthenticationType = CookieAuthenticationDefaults.AuthenticationType,
UseTokenLifetime = false,
AuthenticationType = "OIDC",
Notifications = new OpenIdConnectAuthenticationNotifications
{
SecurityTokenValidated = async tokenValidatedNotification =>
{
try
{
logger.LogTrace("Entering AuthorizationCodeReceived {authTicketIdentity} {response}",
tokenValidatedNotification.AuthenticationTicket.Identity, tokenValidatedNotification.Response.StatusCode);
var claimsToKeep = tokenValidatedNotification.AuthenticationTicket.Identity.Claims.ToList();
claimsToKeep.Add(new Claim("id_token", tokenValidatedNotification.ProtocolMessage.IdToken));
// use the code to get the access and refresh token
#pragma warning disable CS0618 // Type or member is obsolete
var tokenClient = new TokenClient($"{identityServerUrl}/connect/token", clientId, clientSecret);
#pragma warning restore CS0618 // Type or member is obsolete
TokenResponse tokenResponse = await tokenClient.RequestAuthorizationCodeAsync(
tokenValidatedNotification.ProtocolMessage.Code, redirectUrl);
if (tokenResponse.IsError)
{
throw new Exception($"Error authorizing user {tokenResponse.Error}, {tokenResponse.ErrorDescription}", tokenResponse.Exception);
}
// use the access token to retrieve claims from userinfo
#pragma warning disable CS0618 // Type or member is obsolete
var userInfoClient = new UserInfoClient($"{identityServerUrl}/connect/userinfo");
#pragma warning restore CS0618 // Type or member is obsolete
UserInfoResponse userInfoResponse = await userInfoClient.GetAsync(tokenResponse.AccessToken)
.ConfigureAwait(false);
if (userInfoResponse.IsError)
{
throw new Exception(userInfoResponse.Error, userInfoResponse.Exception);
}
claimsToKeep.AddRange(userInfoResponse.Claims);
// create new identity
var claimsIdentity = new ClaimsIdentity(tokenValidatedNotification.AuthenticationTicket
.Identity
.AuthenticationType, "name", "role");
claimsIdentity.AddClaims(userInfoResponse.Claims);
claimsIdentity.AddClaims(claimsToKeep);
var user = claimsIdentity.Claims.FirstOrDefault(x => x.Type == "sub");
if (user != null && user.Value.Contains('@'))
{
var username = user.Value.Split('@')[0];
if (!string.IsNullOrEmpty(username))
{
//remove existing name claim
var removeClaim = claimsIdentity.Claims.ToList().FirstOrDefault(x => x.Type == "name");
if (removeClaim != null)
{
claimsIdentity.RemoveClaim(removeClaim);
}
//add username as name claim
claimsIdentity.AddClaim(new Claim("name", username.ToLower()));
}
}
tokenValidatedNotification.AuthenticationTicket = new AuthenticationTicket(
claimsIdentity,
tokenValidatedNotification.AuthenticationTicket.Properties);
await Task.FromResult(0);
}
catch (Exception ex)
{
logger.LogError(ex, "SecurityTokenValidated error {authTicketIdentity}, {responseStatus} (failure)", tokenValidatedNotification.AuthenticationTicket.Identity, tokenValidatedNotification.Response.StatusCode);
throw;
}
},
RedirectToIdentityProvider = async redirectToIdpNotification =>
{
try
{
if (!BrowserSupportService.isBrowserSupported())
{
redirectToIdpNotification.Response.Redirect("/browserNotSupported");
redirectToIdpNotification.HandleResponse();
}
else
{
// if signing out, add the id_token_hint
if (redirectToIdpNotification.ProtocolMessage.RequestType ==
OpenIdConnectRequestType.Logout)
{
Claim idTokenHint =
redirectToIdpNotification.OwinContext.Authentication.User.FindFirst("id_token");
if (idTokenHint != null)
{
redirectToIdpNotification.ProtocolMessage.IdTokenHint = idTokenHint.Value;
}
redirectToIdpNotification.ProtocolMessage.PostLogoutRedirectUri =
postLogoutRedirectUrl;
redirectToIdpNotification.Options.PostLogoutRedirectUri = postLogoutRedirectUrl;
}
if (redirectToIdpNotification.ProtocolMessage.RequestType ==
OpenIdConnectRequestType.Authentication
&& IsAjaxRequest(redirectToIdpNotification.Request) &&
redirectToIdpNotification.Response.StatusCode == (int) HttpStatusCode.Unauthorized)
{
redirectToIdpNotification.Response.StatusCode = (int) HttpStatusCode.Unauthorized;
redirectToIdpNotification.HandleResponse();
}
}
await Task.FromResult(0);
}
catch (Exception ex)
{
logger.LogError(ex, "RedirectToIdentityProvider error");
throw;
}
}
}
});
日志文件的相关部分
IdentityServer logs are successful, no errors/warnings during this.
解决方案
几年前,我在我工作的公司解决了这个问题。原因是默认的 ASP.Net 身份验证 cookie 解密仅适用于创建/加密 cookie 的服务器。
为了解决这个问题,我很确定你只需要在你的 web.config 文件中添加一个机器密钥——并确保它在两台服务器上都是一样的。
作为第一步,也许尝试复制本文中的值 - 如果可行,则稍后生成您自己的密钥: https ://www.iambacon.co.uk/blog/getting-asp-net-authentication-to-work -on-a-web-farm
推荐阅读
- laravel - 护照Laravel的UUID问题
- temenos-quantum - Kony 没有加载项目。仅加载屏幕
- android - TextAppearance.A 继承自 TextAppearance.AB 不会导致 android 样式中的循环引用?
- nutch - nutch 在索引之前替换解析的内容
- javascript - JavaScript中长数的计算
- react-native - Fetch post method is not working - react native
- android - 如何解决FTP获取excel文件崩溃的问题?
- firebase - Realtime Firebase User id Fetching Problem
- mysql - 如何动态选择列并汇总mysql中的所有大小
- javascript - 如何将输入标签(type='month')的值转换为字符串,如“yyyy-MM”