【问题标题】:saml2 identity provider in Symfony2Symfony2 中的 saml2 身份提供者
【发布时间】:2015-04-16 03:14:21
【问题描述】:

我必须实现一个 SAML2 身份提供者 (IdP) 并将其与现有的 Symfony 2 应用程序集成。

我发现了一些实现服务提供者 (SP) 而不是身份提供者的包,所以我认为我可以使用SimpleSAMLphp library。还有其他解决方案吗?

如何将我的用户提供程序逻辑与 SimpleSAMLphp 集成?

【问题讨论】:

    标签: symfony saml simplesamlphp


    【解决方案1】:

    更新

    正如 Milos Tomic 在他的评论中提到的,airship/lightsaml 被 lightsaml/sp-bundle 取代。你可以找到introduction here。

    ++++++++++++++++++++++++++++

    我最近设置了一个 SAML 解决方案,使用 Simplesamlphp 作为 IDP,SamlSPBundle 作为 SP,一切正常。

    我建议先安装 Simplesamlphp,在 good Documentation here 之后。

    一旦 IDP 启动并运行,您应该会看到一个欢迎页面和一个名为 Federation 的选项卡(或类似的东西,我的安装是德文的)。在那里您应该看到一个选项“SAML 2.0 IdP 元数据”。按照该链接并将显示的 XML 复制到一个单独的文件并保存该文件。

    在 symfony 方面,我创建了一个新的 Bundle 并将其命名为“SamlBundle”。按照文档(步骤 1 和步骤 2)中的说明下载并安装 SamlSPBundle。

    创建您的 SSO 状态/用户类(第 3 步)。这是我如何做到的一个例子:

    namespace SamlBundle\Entity;
    
    use Doctrine\ORM\Mapping as ORM;
    use Symfony\Component\Security\Core\User\UserInterface;
    
    /**
     * @ORM\Entity
     * @ORM\Table(name="samlUser")
     */
    class SamlUser extends \AerialShip\SamlSPBundle\Entity\SSOStateEntity implements UserInterface
    {
    
    /**
     * initialize User object and generates salt for password
     */
    public function __construct()
    {
        if (!$this->userData instanceof UserData) {
            $this->userData  = new UserData();
        }
        $this->setRoles('ROLE_USER');
    }
    
    /**
     * @var int
     * @ORM\Column(type="integer")
     * @ORM\Id
     * @ORM\GeneratedValue(strategy="AUTO")
     */
    protected $id;
    
    /**
     * @var string username
     *
     * @ORM\Column(type="string", length=64, nullable=true)
     */
    protected $username;
    
    /**
     * @var string targetedId
     *
     * @ORM\Column(type="string", length=64, nullable=true, name="targeted_id")
     */
    protected $targetedID;
    
    /**
     * @var string
     * @ORM\Column(type="string", length=32, name="provider_id", nullable=true)
     */
    protected $providerID;
    
    /**
     * @var string
     * @ORM\Column(type="string", length=32, name="auth_svc_name")
     */
    protected $authenticationServiceName;
    
    /**
     * @var string
     * @ORM\Column(type="string", length=64, name="session_index", nullable=true)
     */
    protected $sessionIndex;
    
    /**
     * @var string
     * @ORM\Column(type="string", length=64, name="name_id")
     */
    protected $nameID;
    
    /**
     * @var string
     * @ORM\Column(type="string", length=64, name="name_id_format")
     */
    protected $nameIDFormat;
    
    /**
     * @var \DateTime
     * @ORM\Column(type="datetime", name="created_on")
     */
    protected $createdOn;
    
    /**
     * @var UserData
     * @ORM\OneToOne(targetEntity="UserData", cascade={"all"}, fetch="EAGER")
     * @ORM\JoinColumn(name="user_data", referencedColumnName="id")
     */
    protected $userData;
    

    将您的类添加到 config.yml(第 4 步):

    # app/config/config.yml
    aerial_ship_saml_sp:
        driver: orm
        sso_state_entity_class: SamlBundle\Entity\SamlUser
    

    更新您的 security.yml(第 5 步)。示例;

    providers:
        saml_user_provider:
            id: SamlToState
    
    firewalls:
        dev:
            pattern:  ^/(_(profiler|wdt)|css|images|js)/
            security: false
    
        saml:
            pattern: ^/(?!login_check)
            anonymous: true
            aerial_ship_saml_sp:
                login_path: /saml/sp/login
                check_path: /saml/sp/acs
                logout_path: /saml/sp/logout
                failure_path: /saml/sp/failure
                metadata_path: /saml/sp/FederationMetadata.xml
                discovery_path: /saml/sp/discovery
                local_logout_path: /logout
                provider: saml_user_provider
                create_user_if_not_exists: true
                services:
                    openidp:
                        idp:
                            #the XML-File you saved from the IDP earlier
                            file: "@SamlBundle/Resources/idp-FederationMetadata.xml"
                        sp:
                            config:
                                # required, has to match entity id from IDP XML
                                entity_id: http://your-idp-domain.com
                                # if different then url being used in request
                                # used for construction of assertion consumer and logout urls in SP entity descriptor
                                base_url: http://your-sp-domain.com
                            signing:
                                 #self signed certificate, see [SamlSPBundle docs][4]
                                cert_file: "@SamlBundle/Resources/saml.crt"
                                key_file: "@SamlBundle/Resources/saml.pem"
                                key_pass: ""
                            meta:
                                # must implement SpMetaProviderInterface
                                # id: my.sp.provider.service.id
    
                                # or use builtin SpMetaConfigProvider
                                # any valid saml name id format or shortcuts: persistent or transient
                                name_id_format: transient
                                binding:
                                    # any saml binding or shortcuts: post or redirect
                                    authn_request: redirect
                                    logout_request: redirect
            logout:
                path:   /logout
                target: /
                invalidate_session: true
    

    接下来按照第 6 步中的说明导入路由。在继续第 7 步之前,我建议先创建您的用户提供程序类。这是一个例子:

    namespace SamlBundle\Models;
    
    use SamlBundle\Entity\SamlUser;
    use Symfony\Component\DependencyInjection\ContainerInterface;
    use Symfony\Component\Security\Core\User\UserInterface;
    use AerialShip\SamlSPBundle\Bridge\SamlSpInfo;
    use AerialShip\SamlSPBundle\Security\Core\User\UserManagerInterface as UserManagerInterface;
    
    
    class SamlToState implements UserManagerInterface
    {
    /**
     * @var ContainerInterface   base bundle container
     */
    public $container;
    
    /**
     * Constructor with DependencyInjection params.
     *
     * @param \Symfony\Component\DependencyInjection\ContainerInterface $container
     */
    public function __construct(ContainerInterface $container)  {
        $this->container = $container;
    }
    
    /**
     * {@inheritdoc}
     */
    public function loadUserBySamlInfo(SamlSpInfo $samlInfo)
    {
        $user = $this->loadUserByTargetedID($samlInfo->getAttributes()['eduPersonTargetedID']->getFirstValue());
    
        return $user;
    }
    
    private function loadUserByTargetedID($targetedID) {
        $repository = $this->container->get('doctrine')->getManager()->getRepository('MrmPosSamlBundle:SamlUser');
    
        $user = $repository->findOneBy(
                array('targetedID' => $targetedID)
        );
    
        if ($user) {
            return $user;
        }
    
        throw new \Symfony\Component\Security\Core\Exception\UsernameNotFoundException();
    }
    
    /**
     * {@inheritdoc}
     */
    public function createUserFromSamlInfo(SamlSpInfo $samlInfo)
    {
        $repository = $this->container->get('doctrine')->getManager()->getRepository('MrmPosSamlBundle:SamlUser');
    
        $user = $repository->findOneBy(
                array('nameID' => $samlInfo->getNameID()->getValue())
        );
    
        if ($user) {
            $user->setUsername($samlInfo->getAttributes()['eduPersonPrincipalName']->getFirstValue());
            $user->setTargetedID($samlInfo->getAttributes()['eduPersonTargetedID']->getFirstValue());
            $user->setRoles($samlInfo->getAttributes()['role']->getFirstValue());
        } else {
            $user = new SamlUser();
            $user->setUsername($samlInfo->getAttributes()['eduPersonPrincipalName']->getFirstValue());
            $user->setTargetedID($samlInfo->getAttributes()['eduPersonTargetedID']->getFirstValue());
            $user->setRoles($samlInfo->getAttributes()['role']->getFirstValue());
            $user->setSessionIndex($samlInfo->getAuthnStatement()->getSessionIndex());
    
            $user->setProviderID($samlInfo->getNameID()->getSPProvidedID());
            $user->setAuthenticationServiceName($samlInfo->getAuthenticationServiceID());
            $user->setNameID($samlInfo->getNameID()->getValue());
            $user->setNameIDFormat($samlInfo->getNameID()->getFormat());
        }
    
        $em = $this->container->get('doctrine')->getManager();
        $em->persist($user);
        $em->flush();
    
        return $user;
    }
    
    public function loadUserByUsername($username)
    {
        $repository = $this->container->get('doctrine')->getManager()->getRepository('MrmPosSamlBundle:SamlUser');
    
        $user = $repository->findOneBy(
                array('username' => $username)
        );
    
        if ($user) {
            return $user;
        }
    
        throw new \Symfony\Component\Security\Core\Exception\UsernameNotFoundException();
        return false;
    }
    
    /**
     * {@inheritdoc}
     */
    public function refreshUser(UserInterface $user)
    {
        $repository = $this->container->get('doctrine')->getManager()->getRepository('MrmPosSamlBundle:SamlUser');
    
        $newUser = $repository->findOneBy(
                array('nameID' => $user->getNameID())
        );
    
        if (!$newUser) {
            throw new \Symfony\Component\Security\Core\Exception\UsernameNotFoundException();
        }
    
        return $newUser;
    }
    
    /**
     * {@inheritdoc}
     */
    public function supportsClass($class)
    {
        return true;
    }
    
    }
    

    在 SamlBundle/Resources/config/services.yml 中创建您的服务:

    services:
        SamlToState:
            class: SamlBundle\Models\SamlToState
            arguments: [@service_container]
    

    现在是第 7 步,交换元数据的时候了。获取所描述的 SP XML 并返回到您的 IDP。您可以在“联合”选项卡上找到“XML 到 simpleSAMLphp 元数据转换器”链接。按照该链接,将您的 SP XML 数据转换为 simpleSAMLphp 格式。将该数据添加到 IDP 元数据文件夹中的 saml20-sp-remote.php 文件中。

    好的,我很确定我忘记了什么,但希望这些信息对您有所帮助。如果您遇到困难,欢迎回复我。

    【讨论】:

    • 非常适合 SP 捆绑解决方案!但是关于 IdP 和 SimpleSAMLphp,如何集成我的 symfony 用户提供程序?现在我在 config/authsource.php 中启用了“sqlauth:SQL”,并使用我的自定义原始 SQL 查询来提供用户数据...
    • 在我的项目中,我有某种冗余,这意味着有一个包含 IDP 和 SP 上的用户数据的表。 IDP 处理身份验证,SP 授权。由于用户可以在站点 A 和站点 B 上拥有不同的权限,IDP 只是告诉您,用户已成功登录。所有其他事情都由 SP 处理。跨度>
    • 注意,airship/lightsaml 替换为packagist.org/packages/lightsaml/lightsaml
    【解决方案2】:

    我找到了:

    1. SURFnet SamlBundle 是您找到的库的简单 symfony2 包装器。
    2. SamlSpBundle 更多使用和有据可查。

    看看这两个。第一个非常简单,我不知道是否有足够的文档,当然是一个活跃的项目。第二个似乎更强大且有文档记录,但可能更难以配置。

    希望有帮助

    【讨论】:

    • 谢谢。第二个只实现了SP功能,我先试试。
    • 好的!祝你好运!让我知道这个捆绑包的集成有多复杂!
    • @AlessandroPessina:您是否尝试过使用 SURFnet SamlBundle 来创建 IDP?如果有,难度有多大?文档看起来很稀疏。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2013-02-09
    • 1970-01-01
    • 2021-09-12
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多