You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Sustainsys Saml2设置HandledResult=true仍生双Cookie,如何仅保留自定义认证Cookie?

解决Sustainsys Saml2自定义认证Cookie重复问题

问题原因

你设置了options.SignInScheme = "SamlTemp",这会让Sustainsys Saml2自动将认证后的ClaimsPrincipal登录到该指定Scheme,生成对应的Cookie。即使你在AcsCommandResultCreated中设置commandResult.HandledResult = true,库已经在事件触发前完成了登录到SamlTempScheme的操作,所以最终会同时存在库生成的临时Cookie和你自定义的Cookie。

解决方案

方案1:移除SignInScheme配置

直接删除options.SignInScheme = "SamlTemp"这一行,让库不再自动生成任何认证Cookie,完全由你在事件中处理自定义Cookie和响应逻辑。

方案2:拦截更早的事件(AcsCommandCreated)

如果需要保留SignInScheme的其他作用,可改用AcsCommandCreated事件,在库执行登录操作前就拦截整个流程,自行处理认证和Cookie生成,阻止库执行默认逻辑。

修改后的代码示例

方案1的代码调整

options.SPOptions.ModulePath = samlRoutePrefix;
// 移除SignInScheme配置,避免库自动生成Cookie

options.Notifications.AcsCommandResultCreated = (commandResult, _) =>
{
    var httpContext = httpContextAccessor.HttpContext;
    if (httpContext == null)
        throw new InvalidOperationException("HttpContext not available — ensure IHttpContextAccessor is registered.");

    var completeSamlLogin = httpContext.RequestServices.GetRequiredService<ICompleteSamlLogin>();
    var authenticationCookieContentMapper = httpContext.RequestServices.GetRequiredService<IAuthenticationCookieContentMapper>();

    var relayState = httpContext.Request.Form[SamlConstants.RelayStateKey].ToString();

    var claimsPrincipal = commandResult.Principal;

    var email = ExtractEmailFromClaims(claimsPrincipal);
    if (string.IsNullOrEmpty(email))
        throw new UnauthenticatedException("No email address found in SAML response.");

    var firstName = claimsPrincipal.FindFirst(ClaimTypes.GivenName)?.Value;
    var lastName = claimsPrincipal.FindFirst(ClaimTypes.Surname)?.Value;

    var input = new CompleteSamlLoginInput
    {
        RelayState = relayState,
        Email = email,
        FirstName = firstName,
        LastName = lastName
    };

    var loginOutput = completeSamlLogin.Execute(input).Result;
    var cookieContent = authenticationCookieContentMapper.Map(loginOutput);

    httpContext.CreateAuthenticationCookieAsync(cookieContent, true).GetAwaiter().GetResult();

    commandResult.Headers.Add(SCloudHeaderNames.AntiCsrfToken, cookieContent.AntiCsrfToken.ToString());

    // 设置HandledResult为true,告诉库不要处理后续响应
    commandResult.HandledResult = true;

    // 手动处理RelayState重定向(如果需要)
    if (!string.IsNullOrEmpty(relayState))
    {
        httpContext.Response.Redirect(relayState);
    }
};

方案2的代码示例(使用AcsCommandCreated)

options.SPOptions.ModulePath = samlRoutePrefix;
options.SignInScheme = "SamlTemp"; // 保留该配置的话用此方案

options.Notifications.AcsCommandCreated = (command, _) =>
{
    var httpContext = httpContextAccessor.HttpContext;
    if (httpContext == null)
        throw new InvalidOperationException("HttpContext not available — ensure IHttpContextAccessor is registered.");

    // 从SAML命令中获取认证后的Principal
    var samlCommand = command as Saml2SignInCommand;
    if (samlCommand == null || samlCommand.Principal == null)
        return;

    var completeSamlLogin = httpContext.RequestServices.GetRequiredService<ICompleteSamlLogin>();
    var authenticationCookieContentMapper = httpContext.RequestServices.GetRequiredService<IAuthenticationCookieContentMapper>();

    var relayState = samlCommand.RelayState;

    var claimsPrincipal = samlCommand.Principal;

    var email = ExtractEmailFromClaims(claimsPrincipal);
    if (string.IsNullOrEmpty(email))
        throw new UnauthenticatedException("No email address found in SAML response.");

    var firstName = claimsPrincipal.FindFirst(ClaimTypes.GivenName)?.Value;
    var lastName = claimsPrincipal.FindFirst(ClaimTypes.Surname)?.Value;

    var input = new CompleteSamlLoginInput
    {
        RelayState = relayState,
        Email = email,
        FirstName = firstName,
        LastName = lastName
    };

    var loginOutput = completeSamlLogin.Execute(input).Result;
    var cookieContent = authenticationCookieContentMapper.Map(loginOutput);

    httpContext.CreateAuthenticationCookieAsync(cookieContent, true).GetAwaiter().GetResult();

    httpContext.Response.Headers.Add(SCloudHeaderNames.AntiCsrfToken, cookieContent.AntiCsrfToken.ToString());

    // 手动处理重定向
    if (!string.IsNullOrEmpty(relayState))
    {
        httpContext.Response.Redirect(relayState);
    }
    else
    {
        httpContext.Response.Redirect("/"); // 默认重定向地址
    }

    // 设置Handled为true,阻止库执行后续默认逻辑
    command.Handled = true;
};

关键说明

  • 移除SignInScheme是最直接的方式,因为该配置的核心作用就是让库自动将用户登录到指定Scheme并生成Cookie,而你需要完全自定义Cookie,所以不需要这个自动行为。
  • 如果必须保留SignInScheme(比如用于其他中间件交互),则使用AcsCommandCreated事件,在库执行登录操作前就拦截,自行处理所有流程,并设置command.Handled = true阻止默认逻辑。

内容的提问来源于stack exchange,提问作者user2412672

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.06.12 02:23:17