fengling-auth-service/Controllers/LogoutController.cs
2026-02-21 15:05:37 +08:00

133 lines
5.3 KiB
C#
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

using Fengling.Platform.Domain.AggregatesModel.UserAggregate;
using Fengling.Platform.Domain.AggregatesModel.RoleAggregate;
using Fengling.Platform.Infrastructure;
using Microsoft.AspNetCore;
using Microsoft.AspNetCore.Authentication;
using Microsoft.AspNetCore.Authentication.Cookies;
using Microsoft.AspNetCore.Identity;
using Microsoft.AspNetCore.Mvc;
using OpenIddict.Abstractions;
using OpenIddict.Server.AspNetCore;
using static OpenIddict.Abstractions.OpenIddictConstants;
using System.Linq;
namespace Fengling.AuthService.Controllers;
[ApiController]
[Route("connect")]
public class LogoutController : ControllerBase
{
private readonly IOpenIddictApplicationManager _applicationManager;
private readonly IOpenIddictAuthorizationManager _authorizationManager;
private readonly UserManager<ApplicationUser> _userManager;
private readonly SignInManager<ApplicationUser> _signInManager;
private readonly ILogger<LogoutController> _logger;
public LogoutController(
IOpenIddictApplicationManager applicationManager,
IOpenIddictAuthorizationManager authorizationManager,
UserManager<ApplicationUser> userManager,
SignInManager<ApplicationUser> signInManager,
ILogger<LogoutController> logger)
{
_applicationManager = applicationManager;
_authorizationManager = authorizationManager;
_userManager = userManager;
_signInManager = signInManager;
_logger = logger;
}
[HttpGet("endsession")]
[HttpPost("endsession")]
[IgnoreAntiforgeryToken]
public async Task<IActionResult> EndSession()
{
var request = HttpContext.GetOpenIddictServerRequest() ??
throw new InvalidOperationException("OpenIddict request is null");
// 标准做法:先尝试从 id_token_hint 中提取客户端信息
string? clientId = request.ClientId;
if (string.IsNullOrEmpty(clientId) && !string.IsNullOrEmpty(request.IdTokenHint))
{
try
{
// 从 id_token_hint 中提取 client_id (azp claim 或 aud claim)
var principal = (await HttpContext.AuthenticateAsync(OpenIddictServerAspNetCoreDefaults.AuthenticationScheme)).Principal;
if (principal != null)
{
// 尝试从 azp (authorized party) claim 获取
clientId = principal.GetClaim(Claims.AuthorizedParty);
// 如果没有 azp尝试从 aud (audience) claim 获取
if (string.IsNullOrEmpty(clientId))
{
clientId = principal.GetClaim(Claims.Audience);
}
}
}
catch (Exception ex)
{
_logger.LogWarning(ex, "Failed to extract client_id from id_token_hint");
}
}
// 执行登出
var result = await HttpContext.AuthenticateAsync(IdentityConstants.ApplicationScheme);
if (result.Succeeded)
{
await _signInManager.SignOutAsync();
}
// 处理 post_logout_redirect_uri
if (!string.IsNullOrEmpty(clientId))
{
var application = await _applicationManager.FindByClientIdAsync(clientId);
if (application != null)
{
var registeredUris = await _applicationManager.GetPostLogoutRedirectUrisAsync(application);
// 如果提供了 post_logout_redirect_uri验证它是否在注册的 URI 列表中
if (!string.IsNullOrEmpty(request.PostLogoutRedirectUri))
{
if (registeredUris.Contains(request.PostLogoutRedirectUri))
{
// 如果提供了 state需要附加到重定向 URI
var redirectUri = request.PostLogoutRedirectUri;
if (!string.IsNullOrEmpty(request.State))
{
var separator = redirectUri.Contains('?') ? "&" : "?";
redirectUri = $"{redirectUri}{separator}state={Uri.EscapeDataString(request.State)}";
}
return Redirect(redirectUri);
}
else
{
_logger.LogWarning(
"Post-logout redirect URI {Uri} is not registered for client {ClientId}",
request.PostLogoutRedirectUri,
clientId);
}
}
else
{
// 如果没有提供 post_logout_redirect_uri使用第一个注册的 URI
var defaultUri = registeredUris.FirstOrDefault();
if (!string.IsNullOrEmpty(defaultUri))
{
_logger.LogInformation(
"Using default post-logout redirect URI for client {ClientId}",
clientId);
return Redirect(defaultUri);
}
}
}
}
// 如果无法确定重定向地址,返回默认页面
return Redirect("/");
}
}