【问题标题】:Sitecore 9 Federated Authentication with IdentityServer3, Endless LoopSitecore 9 联合身份验证与 IdentityServer3,无限循环
【发布时间】:2017-11-21 04:54:16
【问题描述】:

我一直在努力让联合身份验证与 Sitecore 9 一起使用 IdentityServer 3 作为 IDP。我已经按照http://blog.baslijten.com/enable-federated-authentication-and-configure-auth0-as-an-identity-provider-in-sitecore-9-0/ 中看到的 Auth0 示例,并将其转换为 IDS3。但我所经历的是 IDP 和 Sitecore 之间的无限循环。

似乎身份验证后,IdentityServer 3 重定向回 Sitecore,它无法将身份验证转换为 cookie。我只剩下一个 .nonce cookie。 Sitecore 没有看到经过身份验证的用户,因此重定向到 IDP,并且这种情况一直持续到我停止该过程为止。

我的 IdentityProviderProcessor(带有虚拟值):

using System.Threading.Tasks;
using Microsoft.Owin.Security.Cookies;
using Microsoft.Owin.Security.OpenIdConnect;
using Owin;
using Sitecore.Diagnostics;
using Sitecore.Owin.Authentication.Configuration;
using Sitecore.Owin.Authentication.Pipelines.IdentityProviders;
using Sitecore.Owin.Authentication.Services;

namespace xx.xxxx.SC.Foundation.Authentication
{
    public class IdentityProviderProcessor : IdentityProvidersProcessor
    {
        public IdentityProviderProcessor(FederatedAuthenticationConfiguration federatedAuthenticationConfiguration) : base(federatedAuthenticationConfiguration)
        {

        }

        /// <summary>
        /// Identityprovidr name. Has to match the configuration
        /// </summary>
        protected override string IdentityProviderName
        {
            get { return "ids3"; }
        }

        protected override void ProcessCore(IdentityProvidersArgs args)
        {
            Assert.ArgumentNotNull(args, "args");
            IdentityProvider identityProvider = this.GetIdentityProvider();
            string authenticationType = this.GetAuthenticationType();

            args.App.UseCookieAuthentication(new CookieAuthenticationOptions
            {
                AuthenticationType = "Cookies"
            });

            args.App.UseOpenIdConnectAuthentication(new OpenIdConnectAuthenticationOptions
            {
                Authority = "xxxx",
                ClientId = "xxxx",
                Scope = "openid profile xxxx",
                RedirectUri = "xxxx",
                ResponseType = "id_token token",
                SignInAsAuthenticationType = "Cookies",
                UseTokenLifetime = false,
                Notifications = new OpenIdConnectAuthenticationNotifications
                {
                    SecurityTokenValidated = (context) =>
                    {
                        var identity = context.AuthenticationTicket.Identity;

                        foreach (Transformation current in identityProvider.Transformations)
                        {
                            current.Transform(identity, new TransformationContext(FederatedAuthenticationConfiguration, identityProvider));
                        }

                        var virtualUser = Sitecore.Security.Authentication.AuthenticationManager.BuildVirtualUser("xxxx\\user@domain.com", true);

                        // You can add roles to the Virtual user
                       virtualUser.Roles.Add(Sitecore.Security.Accounts.Role.FromName("extranet\\MyRole"));

                        // You can even work with the profile if you wish
                        virtualUser.Profile.SetCustomProperty("CustomProperty", "12345");
                        virtualUser.Profile.Email = "user@domain.com";
                        virtualUser.Profile.Name = "My User";

                        // Login the virtual user
                        Sitecore.Security.Authentication.AuthenticationManager.LoginVirtualUser(virtualUser);

                        return Task.FromResult(0);
                    },
                },
            });
        }
    }
}

还有我的配置文件:

<?xml version="1.0" encoding="utf-8"?>
<configuration xmlns:patch="http://www.sitecore.net/xmlconfig/" xmlns:role="http://www.sitecore.net/xmlconfig/role/">
  <sitecore role:require="Standalone or ContentDelivery or ContentManagement">

    <pipelines>
      <owin.identityProviders>
        <!-- Processors for coniguring providers. Each provider must have its own processor-->
        <processor type="xx.xxxx.SC.Foundation.Authentication.IdentityProviderProcessor, xx.xxxx.SC.Foundation.Authentication" resolve="true" />
      </owin.identityProviders>
    </pipelines>

    <federatedAuthentication type="Sitecore.Owin.Authentication.Configuration.FederatedAuthenticationConfiguration, Sitecore.Owin.Authentication">
      <!--Provider mappings to sites-->
      <identityProvidersPerSites hint="list:AddIdentityProvidersPerSites">
        <!--The list of providers assigned to all sites-->
        <mapEntry name="all sites" type="Sitecore.Owin.Authentication.Collections.IdentityProvidersPerSitesMapEntry, Sitecore.Owin.Authentication">
          <sites hint="list">
            <sites hint="list">
              <site>modules_website</site>
              <site>website</site>
            </sites>
          </sites>
          <identityProviders hint="list:AddIdentityProvider">
            <identityProvider ref="federatedAuthentication/identityProviders/identityProvider[@id='ids3']" />
          </identityProviders>
          <externalUserBuilder type="Sitecore.Owin.Authentication.Services.DefaultExternalUserBuilder, Sitecore.Owin.Authentication">

            <param desc="isPersistentUser">false</param>

          </externalUserBuilder>
        </mapEntry>

      </identityProvidersPerSites>

      <!--Definitions of providers-->
      <identityProviders hint="list:AddIdentityProvider">
        <!--Auth0 provider-->
        <identityProvider id="ids3" type="Sitecore.Owin.Authentication.Configuration.DefaultIdentityProvider, Sitecore.Owin.Authentication">
          <param desc="name">$(id)</param>
          <param desc="domainManager" type="Sitecore.Abstractions.BaseDomainManager" resolve="true" />
          <!--This text will be showed for button-->
          <caption></caption>
          <icon></icon>
          <!--Domain name which will be added when create a user-->
          <domain>sitecore</domain>
          <!--list of identity transfromations which are applied to the provider when a user signin-->
          <transformations hint="list:AddTransformation">
            <!--SetIdpClaim transformation-->
            <transformation name="set idp claim" ref="federatedAuthentication/sharedTransformations/setIdpClaim" />
          </transformations>
        </identityProvider>
      </identityProviders>
      <sharedTransformations hint="list:AddTransformation">
      </sharedTransformations>
    </federatedAuthentication>
  </sitecore>
</configuration>

请注意,我能做到这一点的唯一方法是在验证时创建一个 VirtualUser。鉴于几乎完全没有关于此主题的文档,我不确定这是否是必要的步骤,或者我的设置方式是否有问题。

目前,VirtualUser 就像一个冠军一样工作,我们很可能会保持这一点。但是,我想知道这里是否需要创建 VirtualUser 还是我做错了什么?

感谢您的任何意见。

【问题讨论】:

  • 你有什么运气吗? Vyacheslav 的回答是正确的,因为它修复了一些配置错误,但实际上并没有解决我/我们的问题,我也最终陷入了只有 nonce cookie 的循环。
  • @JamieGould 您最终找到了可行的解决方案吗?我也遵循了维亚切斯拉夫的回答,但最终出现了重定向循环

标签: sitecore identityserver3


【解决方案1】:

我在您的整体配置中发现了几个问题,但最重要的是第一个(当然必须删除解决方法):

  1. IdentityProvidersProcessor 的实现必须只包含一个中间件来配置对外部提供者的身份验证,例如UseOpenIdConnectAuthenticationUseAuth0AuthenticationUseFacebookAuthentication. 它不能配置cookie 身份验证,因为Sitecore.Owin.Authentication.config 中已经为您完成了:

    <pipelines>
        ...
        <owin.initialize>
            ...
            <processor type="Sitecore.Owin.Authentication.Pipelines.Initialize.CookieAuthentication, Sitecore.Owin.Authentication"
                       resolve="true" patch:before="processor[@method='Authenticate']" />
            ...
        </owin.initialize>
    </pipelines>
    

    注意:如果您需要处理任何 OWIN cookie 身份验证事件,只需使用相应的管道owin.cookieAuthentication.*

    操作:

    1. 删除您的 UseCookieAuthentication 中间件。
    2. 使用string authenticationType = this.GetAuthenticationType(); 设置OpenIdConnectAuthenticationOptions 对象的SignInAsAuthenticationType 属性(您的代码中未使用authenticationType 变量)。
  2. 不是问题,但 Sitecore.Owin.Authentication.Extensions 命名空间中存在一个扩展方法,可以替换整个 foreach 语句:

    notification.AuthenticationTicket.Identity.ApplyClaimsTransformations(new TransformationContext(this.FederatedAuthenticationConfiguration, identityProvider));
    
  3. 您尝试手动构建虚拟用户并对其进行身份验证,但在您解决第一个问题时,Sitecore 会处理它。

    操作:SecurityTokenValidated 处理程序替换为:

    SecurityTokenValidated = notification =>
    {
        notification.AuthenticationTicket.Identity
          .ApplyClaimsTransformations(new TransformationContext(this.FederatedAuthenticationConfiguration, identityProvider));
        return Task.CompletedTask;
    }
    
  4. 另一个“不是问题,但是..”:非 RTM Sitecore 9.0 版本就是这种情况,但现在您不需要为每个身份提供者手动指定 setIdpClaim 转换。在 federatedAuthentication/sharedTransformations 节点中指定的所有转换都会为所有身份提供者自动执行。

    操作:移除额外的转换

    <!--SetIdpClaim transformation-->
    <transformation name="set idp claim" ref="federatedAuthentication/sharedTransformations/setIdpClaim" />
    
  5. 确保RedirectUri 属性中的值正确。它必须出现在相应 IdentityServer3.Core.Models.Client 对象的 RedirectUris 属性中。


一切完成后,您的外部用户将通过身份验证,但尚未分配任何角色。满足以下任一条件时,用户已分配角色:

  1. 用户存在于数据库中,并在其中分配了角色。
  2. 用户的 ClaimsIdentity 对象具有“http://schemas.microsoft.com/ws/2008/06/identity/claims/role”类型的声明。

分配角色声明的最佳方式是使用声明转换。

示例: 假设您要将 sitecore\Developer 角色分配给包含在对象 ID 为 3e12be6e-58af-479a-a4dc-7a3d5ef61c71 的组中的所有 Azure AD 用户。 AzureAD 身份提供者的声明转换如下所示:

    <transformation name="developer role" type="Sitecore.Owin.Authentication.Services.DefaultTransformation,Sitecore.Owin.Authentication">
        <sources hint="raw:AddSource">
            <claim name="groups" value="3e12be6e-58af-479a-a4dc-7a3d5ef61c70" />
        </sources>
        <targets hint="raw:AddTarget">
            <claim name="http://schemas.microsoft.com/ws/2008/06/identity/claims/role" value="sitecore\Developer " />
         </targets>
    </transformation>

重要提示:默认情况下,AzureAD 不会发回组声明。您需要在应用清单中将groupMembershipClaims 的值设置为SecurityGroup


现在您的用户具有角色,但其个人资料未填满。 与声明转换不同,属性映射配置在所有身份提供者之间共享。 其背后的总体思路是为不同的身份提供者应用个性化的声明转换,并接收具有您期望看到的声明类型的“规范化”ClaimsIdentity。

例如,第一个提供者为您提供“姓氏”声明,第二个提供者提供“姓氏”,第三个提供者提供“http://schemas.xmlsoap.org/ws/2005/05/identity/claims/surname”。然后,您为相应的提供者编写两个声明转换,将“第二个名字”映射到“姓氏”,并将“http://schemas.xmlsoap.org/ws/2005/05/identity/claims/surname”映射到“姓氏”:

在第一个提供者配置节点中:

    <transformation name="surname" type="Sitecore.Owin.Authentication.Services.DefaultTransformation,Sitecore.Owin.Authentication">
        <sources hint="raw:AddSource">
            <claim name="second name" />
        </sources>
        <targets hint="raw:AddTarget">
            <claim name="surname" />
        </targets>
    </transformation>

在第二个提供者配置节点中:

    <transformation name="surname" type="Sitecore.Owin.Authentication.Services.DefaultTransformation,Sitecore.Owin.Authentication">
        <sources hint="raw:AddSource">
            <claim name="http://schemas.xmlsoap.org/ws/2005/05/identity/claims/surname" />
        </sources>
        <targets hint="raw:AddTarget">
            <claim name="surname" />
        </targets>
    </transformation>

现在,你有了一个规范化的 ClaimsIdentity,你可以写一个属性映射:

    <map name="surname" type="Sitecore.Owin.Authentication.Services.DefaultClaimToPropertyMapper, Sitecore.Owin.Authentication" resolve="true">
      <data hint="raw:AddData">
        <source name="surname" />
        <target name="Surname" />
      </data>
    </map>

可以使用user.Profile["Surname"] 读取用户配置文件数据。

注意:如果您需要,可以轻松实现自定义声明转换和属性映射。

【讨论】:

猜你喜欢
  • 2018-03-05
  • 2015-08-11
  • 1970-01-01
  • 2023-03-25
  • 1970-01-01
  • 2011-09-02
  • 1970-01-01
  • 2022-10-14
  • 1970-01-01
相关资源
最近更新 更多