问题描述
我们有一个自产的Webapp A和一个第三方Webapp B.它们都是Windows 2019 Datacenter上本地ADFS 4.0服务器中的依赖方。
Web应用程序A使用WS联合身份验证,Web应用程序B可能使用SAML 2.0,但不是100%确定。 Webapp A没有签名证书。 Webapp B具有有效的签名证书。
用户可以登录webapp A和webapp B并退出,而不会出现任何问题,只要这发生在不同的浏览器会话中即可。
但是,如果用户在webapp A中,并且打开另一个浏览器标签以转到webapp B,并尝试从webapp A登出,则会收到错误消息“ MSIS7054:SAML注销未完成正确。”
并且ADFS事件查看器显示以下异常:
The Federation Service encountered an error while processing the SAML authentication request.
Additional Data
Exception details:
Microsoft.IdentityModel.Protocols.XmlSignature.SignatureVerificationFailedException: ID4037: The key needed to verify the signature Could not be resolved from the following security key identifier 'SecurityKeyIdentifier
(
IsReadOnly = False,Count = 1,Clause[0] = Microsoft.IdentityServer.Tokens.MSISSecurityKeyIdentifierClause
)
'. Ensure that the SecurityTokenResolver is populated with the required key.
at Microsoft.IdentityModel.Protocols.XmlSignature.EnvelopedSignatureReader.ResolveSigningCredentials()
at Microsoft.IdentityModel.Protocols.XmlSignature.EnvelopedSignatureReader.OnEndOfRootElement()
at Microsoft.IdentityModel.Protocols.XmlSignature.EnvelopedSignatureReader.Read()
at System.Xml.XmlReader.ReadEndElement()
at Microsoft.IdentityServer.Protocols.Saml.SamlProtocolSerializer.ReadlogoutRequest(XmlReader reader)
at Microsoft.IdentityServer.Protocols.Saml.HttpSamlBindingSerializer.ReadProtocolMessage(String encodedSamlMessage)
at Microsoft.IdentityServer.Protocols.Saml.Contract.SamlContractUtility.CreateSamlMessage(MSISSamlBindingMessage message)
at Microsoft.IdentityServer.Web.Protocols.Saml.SamlProtocolManager.logout(HttpSamlMessage logoutMessage,String sessionState,String logoutState,Boolean partiallogout,Boolean isUrlTranslationNeeded,HttpSamlMessage& newlogoutMessage,String& newSessionState,String& newlogoutState,Boolean& validlogoutRequest)
Webapp A是一个dotnet核心MVC应用程序。这是退出代码:
[Authorize]
public async Task SignOut()
{
//redirect to /signoutcallback after signout
await SignOutCustom("/signoutcallback");
}
[Authorize]
public async Task SignOutCustom(string redirectUri)
{
await HttpContext.SignOutAsync("Cookies");
var prop = new AuthenticationProperties { RedirectUri = redirectUri };
//redirect to provided target
await HttpContext.SignOutAsync("WsFederation",prop);
}
[AllowAnonymous]
public ActionResult SignOutCallback()
{
if (User.Identity.IsAuthenticated)
{
// Redirect to home page if the user is authenticated.
return RedirectToAction("Index","Home");
}
return View();
}
Startup.cs
public void ConfigureServices(IServiceCollection services)
{
services.AddAuthentication(sharedOptions =>
{
sharedOptions.DefaultScheme = CookieAuthenticationDefaults.AuthenticationScheme;
sharedOptions.DefaultChallengeScheme = WsFederationDefaults.AuthenticationScheme;
})
.AddWsFederation(options =>
{
options.Wtrealm = Configuration["Federation:idaWtrealm"];
options.MetadataAddress = Configuration["Federation:idaADFSMetadata"];
})
.AddCookie();
Microsoft.IdentityModel.Logging.IdentityModelEventSource.ShowPII = true;
services.AddControllersWithViews(options =>
{
var policy = new AuthorizationPolicyBuilder()
.RequireAuthenticatedUser()
.Build();
options.Filters.Add(new Authorizefilter(policy));
});
services.AddRazorPages();
services.AddRouting(options => options.LowercaseUrls = true);
services.AddHttpContextAccessor();
// Add functionality to inject IOptions<T>
services.AddOptions();
// Add our Config object so it can be injected
services.Configure<EndpointOptions>(Configuration.GetSection("Endpoints"));
}
public void Configure(IApplicationBuilder app,IWebHostEnvironment env)
{
if (env.IsDevelopment())
{
app.UseDeveloperExceptionPage();
}
else
{
app.UseExceptionHandler("/home/error");
}
app.UseHttpsRedirection();
app.UseStaticFiles();
app.UseRouting();
app.UseAuthentication();
app.UseAuthorization();
app.UseEndpoints(endpoints =>
{
endpoints.MapControllerRoute(
name: "default",pattern: "{controller=Home}/{action=Index}");
endpoints.MapRazorPages();
});
}
解决方法
确保将Webapp B的证书安装到服务器Webapp A运行的证书存储中。在Webapp A的Web.config中,添加证书引用(更新大括号中的值):
<serviceCertificate>
<certificateReference x509FindType="FindByThumbprint" findValue="{{WebappB_Cert_Thumbprint}}" storeLocation="{{LocalMachine}}" storeName="{{My}}" />
</serviceCertificate>