我正在尝试在我的 ASP.Core 3.1.10 应用程序中使用重定向来实现单次注销。我使用了来自https://www.itfoxtec.com/identitysaml2的代码。注销 POST 请求结果为 303
(User.Identity.IsAuthenticated 变为 false),但下一个 GET 请求结果为 400,未发生重定向(未执行 LoggedOut、SingleLogout 方法)。你能帮忙解决这个问题吗?
这是一个失败的GET SAMLRequest:
<saml2p:LogoutRequest xmlns:saml2p="urn:oasis:names:tc:SAML:2.0:protocol" xmlns:saml2="urn:oasis:names:tc:SAML:2.0:assertion" ID="_40c55084-a748-42be-ba8f-c5bb70010109" Version="2.0" IssueInstant="2021-07-12T20:55:39.5289450Z" Destination="http://myserver:5000/auth/realms/mycompany/protocol/saml" NotOnOrAfter="2021-07-12T21:05:39.5289589Z"><saml2:Issuer>mycompany-dev</saml2:Issuer><saml2:NameID Format="urn:oasis:names:tc:SAML:1.1:nameid-format:unspecified">user1234</saml2:NameID><saml2p:SessionIndex>c8c8b602-34d0-4eb1-aed4-71ae7a06a4b5::0e88e5c4-ce48-44a4-916c-5587a7fdb636</saml2p:SessionIndex></saml2p:LogoutRequest>
SAML2 配置:
saml2Configuration.SignAuthnRequest = true;
saml2Configuration.SigningCertificate = CertificateUtil.Load(AppEnvironment.MapToPhysicalFilePath(Configuration["Saml2:SigningCertificateFile"]), Configuration["Saml2:SigningCertificatePassword"]);
saml2Configuration.AllowedAudienceUris.Add(saml2Configuration.Issuer);
var entityDescriptor = new EntityDescriptor();
entityDescriptor.ReadIdPSsoDescriptorFromFile(Configuration["Saml2:IdPMetadataFile"]);
if (entityDescriptor.IdPSsoDescriptor != null)
{
saml2Configuration.AllowedIssuer = entityDescriptor.EntityId;
saml2Configuration.SingleSignOnDestination = entityDescriptor.IdPSsoDescriptor.SingleSignOnServices.First().Location;
if (AppEnvironment.IsDevelopment() || AppEnvironment.IsStaging()) // todo test
{
saml2Configuration.SingleLogoutDestination = entityDescriptor.IdPSsoDescriptor.SingleLogoutServices.First().Location;
}
saml2Configuration.SignatureValidationCertificates.AddRange(entityDescriptor.IdPSsoDescriptor.SigningCertificates);
}
else
{
throw new Exception("IdPSsoDescriptor not loaded from metadata.");
}
AuthController 登录:
[Route("Login")]
[HttpGet]
public IActionResult Login(string returnUrl = null)
{
var binding = new Saml2RedirectBinding();
binding.SetRelayStateQuery(new Dictionary<string, string> { { relayStateReturnUrlKey, returnUrl ?? Url.Content("~/") } });
var assertionConsumerServiceUrl = new Uri(GetBaseUri(false, false), Url.Content("~/Auth/AssertionConsumerService"));
return binding.Bind(new Saml2AuthnRequest(config)
{
//Subject = new Subject { NameID = new NameID { ID = "username" } },
AssertionConsumerServiceUrl = Startup.AppEnvironment.IsDevelopment() ? assertionConsumerServiceUrl : null
}).ToActionResult();
}enter code here
断言消费者服务:
[Route("AssertionConsumerService")]
[HttpPost]
public async Task<IActionResult> AssertionConsumerService()
{
var binding = new Saml2PostBinding();
var saml2AuthnResponse = new Saml2AuthnResponse(config);
binding.ReadSamlResponse(Request.ToGenericHttpRequest(), saml2AuthnResponse);
if (saml2AuthnResponse.Status != Saml2StatusCodes.Success)
{
throw new AuthenticationException($"SAML Response status: {saml2AuthnResponse.Status}");
}
binding.Unbind(Request.ToGenericHttpRequest(), saml2AuthnResponse);
ClaimsTransform.InitUserService(_userService);
await saml2AuthnResponse.CreateSession(HttpContext, new TimeSpan(24,0,0), true, claimsTransform: (claimsPrincipal) => ClaimsTransform.Transform(claimsPrincipal).Result);
var relayStateQuery = binding.GetRelayStateQuery();
var returnUrl = relayStateQuery.ContainsKey(relayStateReturnUrlKey) ? relayStateQuery[relayStateReturnUrlKey] : Url.Content("~/");
return Redirect(returnUrl);
}
注销:
[HttpPost("Logout")]
public async Task<IActionResult> Logout()
{
if (!User.Identity.IsAuthenticated)
{
return Redirect(Url.Content("~/"));
}
var binding = new Saml2RedirectBinding();
var saml2LogoutRequest = await new Saml2LogoutRequest(config, User).DeleteSession(HttpContext);
return binding.Bind(saml2LogoutRequest).ToActionResult();
}
登出:
[Route("LoggedOut")]
public IActionResult LoggedOut()
{
var binding = new Saml2PostBinding();
binding.Unbind(Request.ToGenericHttpRequest(), new Saml2LogoutResponse(config));
return Redirect(Url.Content("~/"));
}
单次注销:
[Route("SingleLogout")]
public async Task<IActionResult> SingleLogout()
{
Saml2StatusCodes status;
var requestBinding = new Saml2RedirectBinding();
var logoutRequest = new Saml2LogoutRequest(config, User);
try
{
requestBinding.Unbind(Request.ToGenericHttpRequest(), logoutRequest);
status = Saml2StatusCodes.Success;
await logoutRequest.DeleteSession(HttpContext);
}
catch (Exception exc)
{
Debug.WriteLine("SingleLogout error: " + exc.ToString());
status = Saml2StatusCodes.RequestDenied;
}
var responsebinding = new Saml2RedirectBinding();
responsebinding.RelayState = requestBinding.RelayState;
var saml2LogoutResponse = new Saml2LogoutResponse(config)
{
InResponseToAsString = logoutRequest.IdAsString,
Status = status,
};
return responsebinding.Bind(saml2LogoutResponse).ToActionResult();
}