workivate/php-saml

OneLogin PHP SAML Toolkit 的 Workivate 版本

4.1.0 2022-08-23 08:34 UTC

README

此存储库是从 OneLogin 的 php-saml 存储库分叉而来(见下文)

上游存储库的 Master 分支似乎不是管理发布的方式,但在 Lifeworks 存储库中,我们维护了功能分支 -> 开发 -> 主分支的常规发布流程。

版本

2022-08-22 : 作为将所有 PHP 代码库升级到 PHP 8.1(特别是 wa-sso)的准备工作,此包已与上游分支的最新更改同步,版本号为 4.1.0

备注

截至 2022 年 8 月,上游不被视为处于积极开发状态

SAML-Toolkits#531

为了使 PHPUnit 测试通过,已包含 LifeWork Develop 分支中维护的一些更改。

单元测试和静态分析

此包没有 Docker 容器,因此必须从本地开发环境运行 PHPUnit 和 PHPStan。

需要 PHP 8.1

####PHPUnit 运行单元测试。这包括 LifeWorks 存储库中维护的更改

./vendor/bin/phpunit

####PHPStan PHPStan 已作为 PHP 8.1 和 Tag 4.1 升级工作的一部分添加。

./vendor/bin/phpstan analyse --memory-limit=-1 -c phpstan.neon

使用方法

与其他添加到 Lifeworks GemFury 存储库的 php 包不同,php-saml 直接从 GitHub 存储库包含。随着这次升级,该包将添加到 GemFury,以符合 Lifeworks 的最佳实践。

GemFury 是首选的包存储库,因为它适用于本地开发和 CI/CD 管道。

因此,请在任何更改后记住以下事项。

更新 composer.json 中的版本 在 Github 中创建与新版本匹配的新标签和发布 手动下载生成的发布 ZIP 文件并将其上传到 Lifeworks GemFury 账户 运行 Composer

composer require "workivate/php-saml":"^4.1"

OneLogin 的 SAML PHP Toolkit 兼容 PHP 7.X & 8.X

Build Status Coverage Status License

使用此库将 SAML 支持添加到您的 PHP 软件。忘记那些复杂的库,并使用 OneLogin Inc. 提供和支持的此开源库。

警告

此版本与 PHP >=7.3 和 8.X 兼容,并且不包括 xmlseclibs(您需要通过 composer 安装它,依赖关系在 composer.json 中描述)

安全指南

如果您认为您在此工具包中发现了一个安全漏洞,请描述并报告至 https://www.onelogin.com/security。我们遵循负责任的披露指南,并将与您合作迅速找到解决方案。

为什么要在我的软件中添加 SAML 支持?

SAML 是一种基于 XML 的用于网络浏览器单点登录的标准,由 OASIS 安全服务技术委员会定义。该标准自 2002 年以来一直存在,但近年来由于其优势而变得越来越受欢迎。

  • 可用性 - 从门户网站或内部网一键访问、深层链接、消除密码和自动续期会话使用户的生活更加便捷。
  • 安全性 - 基于强大的数字签名进行身份验证和完整性,SAML 是一种安全的单点登录协议,全球最大和最注重安全的企业的首选。
  • 速度 - SAML 很快。只需一次浏览器重定向即可安全地将用户签到应用程序。
  • 钓鱼预防 - 如果您没有应用的密码,您就不会被欺骗在伪造的登录页面上输入密码。
  • IT友好 - SAML简化了IT人员的生活,因为它集中了身份验证,提供了更大的可见性,并使目录集成变得更容易。
  • 机会 - B2B云服务提供商应支持SAML,以促进其产品与SAML的集成。

一般描述

OneLogin的SAML PHP工具包让您可以在PHP应用上构建SP(服务提供商),并将其连接到任何IdP(身份提供者)。

支持

  • SSO和SLO(SP启动和IdP启动)。
  • 断言和nameId加密。
  • 断言签名。
  • 消息签名:AuthNRequest、LogoutRequest、LogoutResponses。
  • 启用断言消费者服务端点。
  • 启用单一注销服务端点。
  • 发布SP元数据(可以进行签名)。

主要功能

  • saml2int - 实现SAML 2.0 Web浏览器SSO配置文件。
  • 无会话 - 忘记SP与最终应用之间的常见冲突,工具包将委托最终应用中的会话。
  • 易于使用 - 程序员可以编写高级和低级代码,提供2个易于使用的API。
  • 已测试 - 经过彻底测试。
  • 流行 - OneLogin的客户在使用它。许多PHP SAML插件在使用它。

使用本指南在OneLogin集成您的PHP工具包: https://developers.onelogin.com/page/saml-toolkit-for-php

安装

依赖项

  • php >= 5.4 和一些核心扩展,如 php-xmlphp-datephp-zlib
  • openssl。安装openssl库。它处理x509证书。
  • gettext。安装该库及其php驱动程序。它处理翻译。
  • curl。如果您计划使用IdP元数据解析器,请安装该库及其php驱动程序。

代码

选项1.从github克隆仓库

git clone git@github.com:onelogin/php-saml.git

然后拉取3.X.X分支/标签

选项2.从github下载

工具包托管在github上。您可以从以下位置下载

搜索3.X.X版本

将库的核心复制到php应用中。(每个应用都有其结构,因此请花时间找到最佳位置的PHP SAML工具包)。请参阅“将SAML支持添加到我的应用的指南”以了解详情。

请注意,压缩文件只包含主要文件。如果您计划使用演示,请使用选项1。

选项3. Composer

工具包支持 composer。您可以在以下位置找到 onelogin/php-saml 包: https://packagist.org.cn/packages/onelogin/php-saml

为了将saml工具包导入到您的当前php项目中,请执行以下操作

composer require onelogin/php-saml

请记住选择3.X.X分支

安装完成后,您将在 vendor/ 文件夹中找到一个名为 onelogin 的新文件夹,并在其中找到 php-saml。请确保您包含由composer提供的自动加载器。它可以在 vendor/autoload.php 中找到。

重要 在此选项中,x509证书必须存储在 vendor/onelogin/php-saml/certs 中,设置文件存储在 vendor/onelogin/php-saml 中。

当使用 composer update 或类似命令更新包时,您的设置可能会被删除。因此,强烈建议您不要使用设置文件,而是将设置作为数组直接传递给构造函数(本文件后面将解释)。如果您不采用此方法,当使用 composer update 或类似命令更新包时,您的设置可能会被删除。

兼容性

此4.X.X支持PHP >=7.3。

它不与PHP5.6或PHP7.0兼容。

命名空间

如果您正在使用包含命名空间框架(如Symfony)的库,请记住,对类的调用必须通过在开头添加反斜杠(\)来执行,例如,要使用静态方法getSelfURLNoQuery,请使用

\OneLogin\Saml2\Utils::getSelfURLNoQuery()

安全警告

在生产环境中,strict参数必须设置为"true",并且security下的signatureAlgorithmdigestAlgorithm必须设置为SHA1以外的值(请参阅https://shattered.io/)。否则,您的环境不安全,将面临攻击。

在生产环境中,我们强烈建议在设置中注册IdP证书,而不是使用指纹方法。指纹是一个散列,所以最终可能受到碰撞攻击,这可能导致签名验证绕过。其他SAML工具集已弃用该机制,我们保留它以保持兼容性,并且也可以用于测试环境。

避免开放重定向攻击

某些实现将RelayState参数用作在SSO和SLO成功时控制流的方式。因此,用户基本上被重定向到RelayState的值。

如果您在HTTP-Redirect绑定上使用签名验证,则RelayState值的完整性将得到保证,否则,在HTTP-POST绑定中,您不能相信RelayState,因此在执行验证之前,您需要验证其值属于受信任和预期的URL。

有关开放重定向的更多信息,请参阅CWE-601

避免回复攻击

回复攻击基本上是尝试重新使用截获的有效SAML消息来模仿SAML操作(SSO或SLO)。

SAML消息具有有限的生命周期(NotBefore,NotOnOrAfter),这使得此类攻击更加困难,但它们仍然可能发生。

为了防止它们,SP可以保持一个已验证和处理的SAML消息或Assertion ID的列表。这些值只需要存储SAML消息生命周期的持续时间,因此我们不需要存储所有已处理的消息/断言ID,只需存储最近的几个。

OneLogin_Saml2_Auth类包含getLastRequestIDgetLastMessageIdgetLastAssertionId方法来检索ID

检查当前消息/断言的ID是否不在已处理的列表中,将防止回复攻击。

入门指南

了解工具包

新的OneLogin SAML工具包包含不同的文件夹(certsendpointslibdemo等)和一些文件。

让我们开始描述文件夹

certs/

SAML需要x509证书来签名和加密NameIDMessageAssertionMetadata等元素。

如果我们的环境需要签名或加密支持,则此文件夹可能包含SP将使用的x509证书和私钥

  • sp.crt - SP的公钥证书
  • sp.key - SP的私钥

或者我们也可以在设置文件中提供这些数据,在$settings['sp']['x509cert']$settings['sp']['privateKey']中。

有时我们可能需要在SP发布的元数据上添加签名,在这种情况下,我们可以使用之前提到的x509证书或使用新的x.509证书:metadata.crtmetadata.key

如果您正在进行密钥轮换过程并希望在服务提供商元数据中发布该x509证书,请使用sp_new.crt

src/

此文件夹包含工具包的核心,即库

  • Saml2文件夹包含在稍后部分中描述的新版本类和方法。

doc/

此文件夹包含工具包的API文档。

endpoints/

该工具包有三个端点

  • metadata.php - SP元数据发布的位置。
  • acs.php - 断言消费者服务。处理SAML响应。
  • sls.php - 单一注销服务。处理注销请求和注销响应。

您可以使用工具包提供的文件,或添加SAML支持时创建自己的端点文件。请注意,这些端点文件使用工具包基本文件夹的设置文件。

locale/

本地化文件夹包含一些翻译:en_USes_ES 作为概念验证。目前还没有翻译,但最终我们将本地化消息并支持多种语言。

其他重要文件

  • settings_example.php - 用于创建包含工具包基本配置信息的settings.php文件的模板。
  • advanced_settings_example.php - 用于创建包含与安全、联系人以及与SP关联的组织的额外配置信息的advanced_settings.php文件的模板。
  • _toolkit_loader.php - 此文件加载工具包库(SAML2库)。

杂项

  • tests/ - 包含工具包的单元测试。
  • demo1/ - 包含一个具有SAML支持的简单PHP应用程序示例。阅读其中的Readme.txt以获取更多信息。
  • demo2/ - 包含另一个示例。

工作原理

设置

首先,我们需要配置工具包。SP的信息、IdP的信息,以及在某些情况下,配置高级安全问题,如签名和加密。

有两种方式提供设置信息

  • 使用应位于工具包基本文件夹中的settings.php文件。
  • 使用包含设置数据的数组,并将其直接提供给类的构造函数。

有一个模板文件,settings_example.php,您可以复制此文件,重命名并编辑它。

<?php

$settings = array(
    // If 'strict' is True, then the PHP Toolkit will reject unsigned
    // or unencrypted messages if it expects them to be signed or encrypted.
    // Also it will reject the messages if the SAML standard is not strictly
    // followed: Destination, NameId, Conditions ... are validated too.
    'strict' => true,

    // Enable debug mode (to print errors).
    'debug' => false,

    // Set a BaseURL to be used instead of try to guess
    // the BaseURL of the view that process the SAML Message.
    // Ex http://sp.example.com/
    //    http://example.com/sp/
    'baseurl' => null,

    // Service Provider Data that we are deploying.
    'sp' => array(
        // Identifier of the SP entity  (must be a URI)
        'entityId' => '',
        // Specifies info about where and how the <AuthnResponse> message MUST be
        // returned to the requester, in this case our SP.
        'assertionConsumerService' => array(
            // URL Location where the <Response> from the IdP will be returned
            'url' => '',
            // SAML protocol binding to be used when returning the <Response>
            // message. OneLogin Toolkit supports this endpoint for the
            // HTTP-POST binding only.
            'binding' => 'urn:oasis:names:tc:SAML:2.0:bindings:HTTP-POST',
        ),
        // If you need to specify requested attributes, set a
        // attributeConsumingService. nameFormat, attributeValue and
        // friendlyName can be omitted
        "attributeConsumingService"=> array(
                "serviceName" => "SP test",
                "serviceDescription" => "Test Service",
                "requestedAttributes" => array(
                    array(
                        "name" => "",
                        "isRequired" => false,
                        "nameFormat" => "",
                        "friendlyName" => "",
                        "attributeValue" => array()
                    )
                )
        ),
        // Specifies info about where and how the <Logout Response> message MUST be
        // returned to the requester, in this case our SP.
        'singleLogoutService' => array(
            // URL Location where the <Response> from the IdP will be returned
            'url' => '',
            // SAML protocol binding to be used when returning the <Response>
            // message. OneLogin Toolkit supports the HTTP-Redirect binding
            // only for this endpoint.
            'binding' => 'urn:oasis:names:tc:SAML:2.0:bindings:HTTP-Redirect',
        ),
        // Specifies the constraints on the name identifier to be used to
        // represent the requested subject.
        // Take a look on lib/Saml2/Constants.php to see the NameIdFormat supported.
        'NameIDFormat' => 'urn:oasis:names:tc:SAML:1.1:nameid-format:emailAddress',
        // Usually x509cert and privateKey of the SP are provided by files placed at
        // the certs folder. But we can also provide them with the following parameters
        'x509cert' => '',
        'privateKey' => '',

        /*
         * Key rollover
         * If you plan to update the SP x509cert and privateKey
         * you can define here the new x509cert and it will be
         * published on the SP metadata so Identity Providers can
         * read them and get ready for rollover.
         */
        // 'x509certNew' => '',
    ),

    // Identity Provider Data that we want connected with our SP.
    'idp' => array(
        // Identifier of the IdP entity  (must be a URI)
        'entityId' => '',
        // SSO endpoint info of the IdP. (Authentication Request protocol)
        'singleSignOnService' => array(
            // URL Target of the IdP where the Authentication Request Message
            // will be sent.
            'url' => '',
            // SAML protocol binding to be used when returning the <Response>
            // message. OneLogin Toolkit supports the HTTP-Redirect binding
            // only for this endpoint.
            'binding' => 'urn:oasis:names:tc:SAML:2.0:bindings:HTTP-Redirect',
        ),
        // SLO endpoint info of the IdP.
        'singleLogoutService' => array(
            // URL Location of the IdP where SLO Request will be sent.
            'url' => '',
            // URL location of the IdP where SLO Response will be sent (ResponseLocation)
            // if not set, url for the SLO Request will be used
            'responseUrl' => '',
            // SAML protocol binding to be used when returning the <Response>
            // message. OneLogin Toolkit supports the HTTP-Redirect binding
            // only for this endpoint.
            'binding' => 'urn:oasis:names:tc:SAML:2.0:bindings:HTTP-Redirect',
        ),
        // Public x509 certificate of the IdP
        'x509cert' => '',
        /*
         *  Instead of use the whole x509cert you can use a fingerprint in order to
         *  validate a SAMLResponse, but we don't recommend to use that
         *  method on production since is exploitable by a collision attack.
         *  (openssl x509 -noout -fingerprint -in "idp.crt" to generate it,
         *   or add for example the -sha256 , -sha384 or -sha512 parameter)
         *
         *  If a fingerprint is provided, then the certFingerprintAlgorithm is required in order to
         *  let the toolkit know which algorithm was used. Possible values: sha1, sha256, sha384 or sha512
         *  'sha1' is the default value.
         *
         *  Notice that if you want to validate any SAML Message sent by the HTTP-Redirect binding, you
         *  will need to provide the whole x509cert.
         */
        // 'certFingerprint' => '',
        // 'certFingerprintAlgorithm' => 'sha1',

        /* In some scenarios the IdP uses different certificates for
         * signing/encryption, or is under key rollover phase and
         * more than one certificate is published on IdP metadata.
         * In order to handle that the toolkit offers that parameter.
         * (when used, 'x509cert' and 'certFingerprint' values are
         * ignored).
         */
        // 'x509certMulti' => array(
        //      'signing' => array(
        //          0 => '<cert1-string>',
        //      ),
        //      'encryption' => array(
        //          0 => '<cert2-string>',
        //      )
        // ),
    ),
);

除了必需的设置数据(IdP、SP)外,还可以定义其他信息。与存在用于基本信息的模板一样,在工具包基本文件夹中还有一个用于高级信息的模板,名为advanced_settings_example.php,您可以复制并重命名为advanced_settings.php

<?php

$advancedSettings = array(

    // Compression settings
    'compress' => array(
        'requests' => true,
        'responses' => true
    ),
    // Security settings
    'security' => array(

        /** signatures and encryptions offered */

        // Indicates that the nameID of the <samlp:logoutRequest> sent by this SP
        // will be encrypted.
        'nameIdEncrypted' => false,

        // Indicates whether the <samlp:AuthnRequest> messages sent by this SP
        // will be signed.  [Metadata of the SP will offer this info]
        'authnRequestsSigned' => false,

        // Indicates whether the <samlp:logoutRequest> messages sent by this SP
        // will be signed.
        'logoutRequestSigned' => false,

        // Indicates whether the <samlp:logoutResponse> messages sent by this SP
        // will be signed.
        'logoutResponseSigned' => false,

        /* Sign the Metadata
         False || True (use sp certs) || array (
                                                    'keyFileName' => 'metadata.key',
                                                    'certFileName' => 'metadata.crt'
                                               )
                                      || array (
                                                    'x509cert' => '',
                                                    'privateKey' => ''
                                               )
        */
        'signMetadata' => false,

        /** signatures and encryptions required **/

        // Indicates a requirement for the <samlp:Response>, <samlp:LogoutRequest>
        // and <samlp:LogoutResponse> elements received by this SP to be signed.
        'wantMessagesSigned' => false,

        // Indicates a requirement for the <saml:Assertion> elements received by
        // this SP to be encrypted.
        'wantAssertionsEncrypted' => false,

        // Indicates a requirement for the <saml:Assertion> elements received by
        // this SP to be signed. [Metadata of the SP will offer this info]
        'wantAssertionsSigned' => false,

        // Indicates a requirement for the NameID element on the SAMLResponse
        // received by this SP to be present.
        'wantNameId' => true,

        // Indicates a requirement for the NameID received by
        // this SP to be encrypted.
        'wantNameIdEncrypted' => false,

        // Authentication context.
        // Set to false and no AuthContext will be sent in the AuthNRequest.
        // Set true or don't present this parameter and you will get an AuthContext 'exact' 'urn:oasis:names:tc:SAML:2.0:ac:classes:PasswordProtectedTransport'.
        // Set an array with the possible auth context values: array('urn:oasis:names:tc:SAML:2.0:ac:classes:Password', 'urn:oasis:names:tc:SAML:2.0:ac:classes:X509').
        'requestedAuthnContext' => false,

        // Indicates if the SP will validate all received xmls.
        // (In order to validate the xml, 'strict' and 'wantXMLValidation' must be true).
        'wantXMLValidation' => true,

        // If true, SAMLResponses with an empty value at its Destination
        // attribute will not be rejected for this fact.
        'relaxDestinationValidation' => false,

        // If true, Destination URL should strictly match to the address to
        // which the response has been sent.
        // Notice that if 'relaxDestinationValidation' is true an empty Destintation
        // will be accepted.
        'destinationStrictlyMatches' => false,

        // If true, the toolkit will not raised an error when the Statement Element
        // contain atribute elements with name duplicated
        'allowRepeatAttributeName' => false,

        // If true, SAMLResponses with an InResponseTo value will be rejectd if not
        // AuthNRequest ID provided to the validation method.
        'rejectUnsolicitedResponsesWithInResponseTo' => false,

        // Algorithm that the toolkit will use on signing process. Options:
        //    'http://www.w3.org/2000/09/xmldsig#rsa-sha1'
        //    'http://www.w3.org/2000/09/xmldsig#dsa-sha1'
        //    'http://www.w3.org/2001/04/xmldsig-more#rsa-sha256'
        //    'http://www.w3.org/2001/04/xmldsig-more#rsa-sha384'
        //    'http://www.w3.org/2001/04/xmldsig-more#rsa-sha512'
        // Notice that rsa-sha1 is a deprecated algorithm and should not be used
        'signatureAlgorithm' => 'http://www.w3.org/2001/04/xmldsig-more#rsa-sha256',

        // Algorithm that the toolkit will use on digest process. Options:
        //    'http://www.w3.org/2000/09/xmldsig#sha1'
        //    'http://www.w3.org/2001/04/xmlenc#sha256'
        //    'http://www.w3.org/2001/04/xmldsig-more#sha384'
        //    'http://www.w3.org/2001/04/xmlenc#sha512'
        // Notice that sha1 is a deprecated algorithm and should not be used
        'digestAlgorithm' => 'http://www.w3.org/2001/04/xmlenc#sha256',

        // Algorithm that the toolkit will use for encryption process. Options:
        // 'http://www.w3.org/2001/04/xmlenc#tripledes-cbc'
        // 'http://www.w3.org/2001/04/xmlenc#aes128-cbc'
        // 'http://www.w3.org/2001/04/xmlenc#aes192-cbc'
        // 'http://www.w3.org/2001/04/xmlenc#aes256-cbc'
        // 'http://www.w3.org/2009/xmlenc11#aes128-gcm'
        // 'http://www.w3.org/2009/xmlenc11#aes192-gcm'
        // 'http://www.w3.org/2009/xmlenc11#aes256-gcm';
        // Notice that aes-cbc are not consider secure anymore so should not be used
        'encryption_algorithm' => 'http://www.w3.org/2009/xmlenc11#aes128-gcm',

        // ADFS URL-Encodes SAML data as lowercase, and the toolkit by default uses
        // uppercase. Turn it True for ADFS compatibility on signature verification
        'lowercaseUrlencoding' => false,
    ),

    // Contact information template, it is recommended to supply a
    // technical and support contacts.
    'contactPerson' => array(
        'technical' => array(
            'givenName' => '',
            'emailAddress' => ''
        ),
        'support' => array(
            'givenName' => '',
            'emailAddress' => ''
        ),
    ),

    // Organization information template, the info in en_US lang is
    // recomended, add more if required.
    'organization' => array(
        'en-US' => array(
            'name' => '',
            'displayname' => '',
            'url' => ''
        ),
    ),
);

压缩设置允许您指示IdP是否可以接受使用gzip压缩的数据('请求'和'响应')。但是,如果我们将$deflate布尔参数提供给getRequestgetResponse方法,则它将具有优先权。

在安全部分,您可以设置SP处理消息和断言的方式。联系IdP的管理员,并询问IdP期望的方式,并决定SP将处理哪些验证以及SP将有哪些要求,并将这些要求传达给IdP的管理员。

一旦我们知道可以配置哪种类型的数据,让我们谈谈工具包内如何处理设置。

所描述的设置文件(settings.phpadvanced_settings.php)由工具包加载,如果没有在工具包构造函数中提供其他包含设置信息的数组。让我们看看一些例子。

// Initializes toolkit with settings.php & advanced_settings files.
$auth = new OneLogin\Saml2\Auth();
//or
$settings = new OneLogin\Saml2\Settings();

// Initializes toolkit with the array provided.
$auth = new OneLogin\Saml2\Auth($settingsInfo);
//or
$settings = new OneLogin\Saml2\Settings($settingsInfo);

您可以在包含构造函数执行文件的文件中声明$settingsInfo,或将它们放在任何文件中,并加载该文件以获取数组,如下面的例子所示

<?php

require_once 'custom_settings.php';  // The custom_settings.php contains a
                                     // $settingsInfo array.

$auth = new OneLogin\Saml2\Auth($settingsInfo);

如何加载库

为了使用工具库,如果你的项目支持Composer,你只需使用Composer安装它(参见安装部分)就完成了。

如果你的项目不使用Composer,你需要导入位于工具库基本文件夹中的 _toolkit_loader.php 文件。你可以这样加载这个文件

<?php

define("TOOLKIT_PATH", '/var/www/php-saml/');
require_once(TOOLKIT_PATH . '_toolkit_loader.php');

在这行代码之后,我们将能够使用工具库的类(及其方法)(因为外部和Saml2库文件已经加载)。

该工具库依赖于 xmlseclibs 3.X.X分支,你需要获取其代码并将其放置在您的项目中,并重新使用 _toolkit_loader.php 文件以包含 xmlseclibs。

启动SSO

为了向IdP发送 AuthNRequest

<?php

define("TOOLKIT_PATH", '/var/www/php-saml/');
require_once(TOOLKIT_PATH . '_toolkit_loader.php');   // We load the SAML2 lib

$auth = new OneLogin\Saml2\Auth(); // Constructor of the SP, loads settings.php
                                   // and advanced_settings.php
$auth->login();   // Method that sent the AuthNRequest

根据 advanced_settings.php'authnRequestsSigned')的安全信息,AuthNRequest 将被签名或未签名。

然后,IdP将返回SAML响应到用户的客户端。客户端随后将带有此信息转发到SP的属性消费者服务。如果我们没有在登录方法中设置 'url' 参数,并且我们使用工具库提供的默认ACS(endpoints/acs.php),那么ACS端点将重定向用户到启动SSO请求的文件。

我们可以设置一个 'returnTo' url 来更改工作流程并将用户重定向到其他PHP文件。

$newTargetUrl = 'http://example.com/consume2.php';
$auth = new OneLogin\Saml2\Auth();
$auth->login($newTargetUrl);

登录方法可以接收其他六个可选参数

  • $parameters - 一个参数数组,它将在HTTP-Redirect中的 GET 中添加。
  • $forceAuthn - 当为true时,AuthNRequest 将设置 ForceAuthn='true'
  • $isPassive - 当为true时,AuthNRequest 将设置 Ispassive='true'
  • $strict - 如果我们想要保持(返回url字符串),则为true;如果为false,则为重定向
  • $setNameIdPolicy - 当为true时,AuthNRequest 将设置一个nameIdPolicy元素。
  • $nameIdValueReq - 向IdP指示应该进行认证的实体。

如果需要匹配未来的SAMLResponse ID和要发送的AuthNRequest ID,则必须提取并保存该AuthNRequest ID。

$ssoBuiltUrl = $auth->login(null, array(), false, false, true);
$_SESSION['AuthNRequestID'] = $auth->getLastRequestID();
header('Pragma: no-cache');
header('Cache-Control: no-cache, must-revalidate');
header('Location: ' . $ssoBuiltUrl);
exit();

SP端点

与SP相关,有三个重要的视图:元数据视图、ACS视图和SLS视图。工具库在端点目录中提供了这些视图的示例。

SP元数据 endpoints/metadata.php

此代码将提供我们的SP的XML元数据文件,基于我们在设置文件中提供的信息。

<?php

define("TOOLKIT_PATH", '/var/www/php-saml/');
require_once dirname(TOOLKIT_PATH.'/_toolkit_loader.php';

try {
    $auth = new OneLogin\Saml2\Auth();
    $settings = $auth->getSettings();
    $metadata = $settings->getSPMetadata();
    $errors = $settings->validateMetadata($metadata);
    if (empty($errors)) {
        header('Content-Type: text/xml');
        echo $metadata;
    } else {
        throw new OneLogin\Saml2\Error(
            'Invalid SP metadata: '.implode(', ', $errors),
            OneLogin\Saml2\Error::METADATA_SP_INVALID
        );
    }
} catch (Exception $e) {
    echo $e->getMessage();
}

getSPMetadata 将根据 advanced_settings.php'signMetadata')的安全信息返回已签名或未签名的元数据。

在暴露XML元数据之前,会进行检查以确保提供的信息是有效的。

除了使用Auth对象外,您还可以直接使用

$settings = new OneLogin\Saml2\Settings($settingsInfo, true);

来获取设置对象,并通过使用true参数避免IdP设置验证。

属性消费者服务(ACS) endpoints/acs.php

此代码处理IdP通过用户的客户端转发给SP的SAML响应。

<?php

session_start();  // IMPORTANT: This is required in order to be able
                  // to store the user data in the session.

define("TOOLKIT_PATH", '/var/www/php-saml/');
require_once dirname(TOOLKIT_PATH.'/_toolkit_loader.php';

$auth = new OneLogin\Saml2\Auth();

if (isset($_SESSION) && isset($_SESSION['AuthNRequestID'])) {
    $requestID = $_SESSION['AuthNRequestID'];
} else {
    $requestID = null;
}

$auth->processResponse($requestID);
unset($_SESSION['AuthNRequestID']);

$errors = $auth->getErrors();

if (!empty($errors)) {
    echo '<p>' . implode(', ', $errors) . '</p>';
    exit();
}

if (!$auth->isAuthenticated()) {
    echo "<p>Not authenticated</p>";
    exit();
}

$_SESSION['samlUserdata'] = $auth->getAttributes();
$_SESSION['samlNameId'] = $auth->getNameId();
$_SESSION['samlNameIdFormat'] = $auth->getNameIdFormat();
$_SESSION['samlNameidNameQualifier'] = $auth->getNameIdNameQualifier();
$_SESSION['samlNameidSPNameQualifier'] = $auth->getNameIdSPNameQualifier();
$_SESSION['samlSessionIndex'] = $auth->getSessionIndex();

if (isset($_POST['RelayState']) && OneLogin\Saml2\Utils::getSelfURL() != $_POST['RelayState']) {
    // To avoid 'Open Redirect' attacks, before execute the
    // redirection confirm the value of $_POST['RelayState'] is a // trusted URL.
    $auth->redirectTo($_POST['RelayState']);
}

$attributes = $_SESSION['samlUserdata'];
$nameId = $_SESSION['samlNameId'];

echo '<h1>Identified user: '. htmlentities($nameId) .'</h1>';

if (!empty($attributes)) {
    echo '<h2>' . _('User attributes:') . '</h2>';
    echo '<table><thead><th>' . _('Name') . '</th><th>' . _('Values') . '</th></thead><tbody>';
    foreach ($attributes as $attributeName => $attributeValues) {
        echo '<tr><td>' . htmlentities($attributeName) . '</td><td><ul>';
        foreach ($attributeValues as $attributeValue) {
            echo '<li>' . htmlentities($attributeValue) . '</li>';
        }
        echo '</ul></td></tr>';
    }
    echo '</tbody></table>';
} else {
    echo _('No attributes found.');
}

SAML响应被处理并检查是否存在错误。它还验证用户是否已认证并将用户数据存储在会话中。

此时有两种可能的替代方案

  1. 如果没有提供 RelayState,则可以在这个视图中显示用户数据或我们想要的任何方式。

  2. 如果提供了 RelayState,则进行重定向。

请注意,在重定向之前,我们已经将用户数据保存在会话中,以便在RelayState视图中使用用户数据。

getAttributes方法

为了检索属性,我们可以使用

$attributes = $auth->getAttributes();

使用此方法,我们可以获取SAML响应断言中由IdP提供的所有用户数据。

如果我们执行print_r($attributes),我们可以得到

Array
(
    [cn] => Array
        (
            [0] => John
        )
    [sn] => Array
        (
            [0] => Doe
        )
    [mail] => Array
        (
            [0] => john.doe@example.com
        )
    [groups] => Array
        (
            [0] => users
            [1] => members
        )
)

每个属性名都可以用作$attributes的索引以获取值。每个属性值都是一个数组——单值属性是一个只有一个元素的数组。

以下代码是等效的

$attributes = $auth->getAttributes();
print_r($attributes['cn']);
print_r($auth->getAttribute('cn'));

在尝试获取属性之前,检查用户是否已认证。如果用户未认证或SAML断言中没有属性,则将返回一个空数组。例如,如果我们调用getAttributes在调用$auth->processResponse之前,则getAttributes()将返回一个空数组。

单点登出服务(SLS)endpoints/sls.php

此代码处理登出请求和登出响应。

<?php

session_start();  // IMPORTANT: This is required in order to be able
                  // to close the user session.

define("TOOLKIT_PATH", '/var/www/php-saml/');
require_once dirname(TOOLKIT_PATH.'/_toolkit_loader.php';

$auth = new OneLogin\Saml2\Auth();

if (isset($_SESSION) && isset($_SESSION['LogoutRequestID'])) {
    $requestID = $_SESSION['LogoutRequestID'];
} else {
    $requestID = null;
}

$auth->processSLO(false, $requestID);

$errors = $auth->getErrors();

if (empty($errors)) {
    echo 'Sucessfully logged out';
} else {
    echo implode(', ', $errors);
}

如果SLS端点收到登出响应,将验证响应并可以关闭会话

// part of the processSLO method

$logoutResponse = new OneLogin\Saml2\LogoutResponse($this->_settings, $_GET['SAMLResponse']);
if (!$logoutResponse->isValid($requestId)) {
    $this->_errors[] = 'invalid_logout_response';
} else if ($logoutResponse->getStatus() !== OneLogin\Saml2\Constants::STATUS_SUCCESS) {
    $this->_errors[] = 'logout_not_success';
} else {
    if (!$keepLocalSession) {
        OneLogin\Saml2\Utils::deleteLocalSession();
    }
}

如果SLS端点收到登出请求,将验证请求,关闭会话并向IdP的SLS端点发送登出响应。

// part of the processSLO method

$decoded = base64_decode($_GET['SAMLRequest']);
$request = gzinflate($decoded);
if (!OneLogin\Saml2\LogoutRequest::isValid($this->_settings, $request)) {
    $this->_errors[] = 'invalid_logout_request';
} else {
    if (!$keepLocalSession) {
        OneLogin\Saml2\Utils::deleteLocalSession();
    }

    $inResponseTo = $request->id;
    $responseBuilder = new OneLogin\Saml2\LogoutResponse($this->_settings);
    $responseBuilder->build($inResponseTo);
    $logoutResponse = $responseBuilder->getResponse();

    $parameters = array('SAMLResponse' => $logoutResponse);
    if (isset($_GET['RelayState'])) {
        $parameters['RelayState'] = $_GET['RelayState'];
    }

    $security = $this->_settings->getSecurityData();
    if (isset($security['logoutResponseSigned']) && $security['logoutResponseSigned']) {
        $signature = $this->buildResponseSignature($logoutResponse, $parameters['RelayState'], $security['signatureAlgorithm']);
        $parameters['SigAlg'] = $security['signatureAlgorithm'];
        $parameters['Signature'] = $signature;
    }

    $this->redirectTo($this->getSLOurl(), $parameters);
}

如果您不使用默认的PHP会话或需要手动销毁会话,则可以将回调方法传递给processSLO方法作为第四个参数

$keepLocalSession = False;
$callback = function () {
    // Destroy user session
};

$auth->processSLO($keepLocalSession, null, false, $callback);

如果我们不想让processSLO销毁会话,请将一个true参数传递给processSLO方法

$keepLocalSession = True;
$auth->processSLO($keepLocalSession);

启动SLO

为了向IdP发送登出请求

<?php

define("TOOLKIT_PATH", '/var/www/php-saml/');
require_once(TOOLKIT_PATH . '_toolkit_loader.php');

$auth = new OneLogin\Saml2\Auth();

$auth->logout();   // Method that sent the Logout Request.

此外,还可以设置八个可选参数

  • $returnTo - 用户登出后应返回的目标URL。
  • $parameters - 要添加到GET中的额外参数。
  • $name_id - 将用于构建登出请求。如果未设置name_id参数并且auth对象处理了包含NameId的SAML响应,则将使用此NameId
  • $session_index - 指定用户会话的SessionIndex。
  • $stay - 如果我们想停留(返回url字符串)则返回True,如果需要重定向则返回False。
  • $nameIdFormat - NameID格式将在登出请求中设置。
  • $nameIdNameQualifier - NameID NameQualifier将在登出请求中设置。
  • $nameIdSPNameQualifier - NameID SP NameQualifier将在登出请求中设置。

根据advanced_settings.php'logoutRequestSigned')的安全信息,登出请求将根据需要签名或未签名。

IdP将通过用户的客户端将登出响应返回到SP的单点登出服务。如果我们没有在登出方法中设置'url'参数并使用工具包提供的默认SLS(endpoints/sls.php),则SLS端点将重定向用户到启动SLO请求的文件。

我们可以设置一个'returnTo' URL以更改工作流程并重定向用户到其他PHP文件。

$newTargetUrl = 'http://example.com/loggedOut.php';
$auth = new OneLogin\Saml2\Auth();
$auth->logout($newTargetUrl);

具有所有参数的更复杂登出

$auth = new OneLogin\Saml2\Auth();
$returnTo = null;
$parameters = array();
$nameId = null;
$sessionIndex = null;
$nameIdFormat = null;
$nameIdNameQualifier = null;
$nameIdSPNameQualifier = null;

if (isset($_SESSION['samlNameId'])) {
    $nameId = $_SESSION['samlNameId'];
}
if (isset($_SESSION['samlSessionIndex'])) {
    $sessionIndex = $_SESSION['samlSessionIndex'];
}
if (isset($_SESSION['samlNameIdFormat'])) {
    $nameIdFormat = $_SESSION['samlNameIdFormat'];
}
if (isset($_SESSION['samlNameIdNameQualifier'])) {
    $nameIdNameQualifier = $_SESSION['samlNameIdNameQualifier'];
}
if (isset($_SESSION['samlNameIdSPNameQualifier'])) {
    $nameIdSPNameQualifier = $_SESSION['samlNameIdSPNameQualifier'];
}
$auth->logout($returnTo, $parameters, $nameId, $sessionIndex, false, $nameIdFormat, $nameIdNameQualifier, $nameIdSPNameQualifier);

如果需要匹配未来的登出响应ID和要发送的登出请求ID,则必须提取并存储该登出请求ID。

$sloBuiltUrl = $auth->logout(null, $parameters, $nameId, $sessionIndex, true);
$_SESSION['LogoutRequestID'] = $auth->getLastRequestID();
header('Pragma: no-cache');
header('Cache-Control: no-cache, must-revalidate');
header('Location: ' . $sloBuiltUrl);
exit();

示例视图,该视图启动SSO请求并处理响应(是acs目标)

我们可以编写一个独特的文件,用于启动SSO过程,处理响应,获取属性,启动SLO并处理登出响应。

注意:请查看包含该用例的demo1文件夹;在后续部分我们将进一步详细解释demo1用例。

<?php

session_start();    // Initialize the session, we do that because
                    // Note that processResponse and processSLO
                    // methods could manipulate/close that session

require_once dirname(__DIR__) . '/_toolkit_loader.php'; // Load Saml2 and xmlseclibs
require_once 'settings.php';    // Load the setting info as an Array

$auth = new OneLogin\Saml2\Auth($settingsInfo);  // Initialize the SP SAML instance

if (isset($_GET['sso'])) {    // SSO action.  Will send an AuthNRequest to the IdP
    $auth->login();
} else if (isset($_GET['sso2'])) {              // Another SSO action
    $returnTo = $spBaseUrl.'/demo1/attrs.php';  // but set a custom RelayState URL
    $auth->login($returnTo);
} else if (isset($_GET['slo'])) {  // SLO action. Will sent a Logout Request to IdP
    $auth->logout();
} else if (isset($_GET['acs'])) {  // Assertion Consumer Service
    $auth->processResponse();      // Process the Response of the IdP, get the
                                   // attributes and put then at
                                   // $_SESSION['samlUserdata']

    $errors = $auth->getErrors();  // This method receives an array with the errors
                                   // that could took place during the process

    if (!empty($errors)) {
        echo '<p>' . implode(', ', $errors) . '</p>';
    }
                                          // This check if the response was
    if (!$auth->isAuthenticated()) {      // sucessfully validated and the user
        echo '<p>Not authenticated</p>';  // data retrieved or not
        exit();
    }

    $_SESSION['samlUserdata'] = $auth->getAttributes(); // Retrieves user data
    if (isset($_POST['RelayState']) && OneLogin\Saml2\Utils::getSelfURL() != $_POST['RelayState']) {
        // To avoid 'Open Redirect' attacks, before execute the
        // redirection confirm the value of $_POST['RelayState'] is a // trusted URL.
        $auth->redirectTo($_POST['RelayState']);  // Redirect if there is a
    }                                             // relayState set
} else if (isset($_GET['sls'])) {   // Single Logout Service
    $auth->processSLO();            // Process the Logout Request & Logout Response
    $errors = $auth->getErrors(); // Retrieves possible validation errors
    if (empty($errors)) {
        echo '<p>Sucessfully logged out</p>';
    } else {
        echo '<p>' . htmlentities(implode(', ', $errors)) . '</p>';
    }
}

if (isset($_SESSION['samlUserdata'])) {   // If there is user data we print it.
    if (!empty($_SESSION['samlUserdata'])) {
        $attributes = $_SESSION['samlUserdata'];
        echo 'You have the following attributes:<br>';
        echo '<table><thead><th>Name</th><th>Values</th></thead><tbody>';
        foreach ($attributes as $attributeName => $attributeValues) {
            echo '<tr><td>' . htmlentities($attributeName) . '</td><td><ul>';
            foreach ($attributeValues as $attributeValue) {
                echo '<li>' . htmlentities($attributeValue) . '</li>';
            }
            echo '</ul></td></tr>';
        }
        echo '</tbody></table>';
    } else {                             // If there is not user data, we notify
        echo "<p>You don't have any attribute</p>";
    }

    echo '<p><a href="?slo" >Logout</a></p>'; // Print some links with possible
} else {                                      // actions
    echo '<p><a href="?sso" >Login</a></p>';
    echo '<p><a href="?sso2" >Login and access to attrs.php page</a></p>';
}

URL猜测方法

php-saml工具包使用OneLogin\Saml2\Utils中的一系列方法来猜测处理SAML消息的URL。

  • getSelfHost 返回当前主机。
  • getSelfPort 返回用于请求的端口号
  • isHTTPS 检查协议是否为 https 或 http。
  • getSelfURLhost 返回协议 + 当前主机 + 端口(如果与常用端口不同)。
  • getSelfURL 返回当前主机的 URL + 当前视图 + 查询。
  • getSelfURLNoQuery 返回当前主机 + 当前视图的 URL。
  • getSelfRoutedURLNoQuery 返回当前主机 + 当前视图的路由 URL。

getSelfURLNoQuery 和 getSelfRoutedURLNoQuery 用于计算 currentURL,以验证 SAML 元素,如 Destination 或 Recipient。

当 PHP 应用程序位于代理或负载均衡器后面时,我们可以执行 setProxyVars(true)setSelfPort,则 isHTTPS 将处理 $_SERVER["HTTP_X_FORWARDED_PORT"]$_SERVER['HTTP_X_FORWARDED_PROTO'] 变量(否则将忽略它们)。

开发者还可以使用 setSelfProtocolsetSelfHostsetSelfPortgetBaseURLPath 来定义特定值,这些值将由 isHTTPSgetSelfHostgetSelfPortgetBaseURLPath 返回。并定义一个 setBasePath,用于在 getSelfURLgetSelfRoutedURLNoQuery 中使用,以替换从 $_SERVER["REQUEST_URI"] 提取的数据。

在设置中,开发者可以设置一个 'baseurl' 参数,该参数将自动使用 setBaseURL 来为 setSelfProtocolsetSelfHostsetSelfPortsetBaseURLPath 设置值。

在负载均衡器后面工作

当在带有 SSL 转发的负载均衡器后面工作时,断言请求 URL 和 SAML 响应的 Destination 属性可能会失败。

您可以通过配置服务器使其了解代理并返回请求的原始 URL 来解决这个问题。

或者使用上一节中描述的方法。

SP 密钥轮换

如果您计划更新 SP x509cert 和 privateKey,可以将新的 x509cert 定义为 $settings['sp']['x509certNew'],它将在 SP 元数据上发布,以便身份提供者可以读取它们并为轮换做好准备。

具有多个证书的 IdP

在某些情况下,IdP 使用不同的证书进行签名/加密,或者处于密钥轮换阶段,并且在 IdP 元数据上发布了多个证书。

为了处理这种情况,工具包提供了 $settings['idp']['x509certMulti'] 参数。

当使用该参数时,工具包将忽略 'x509cert''certFingerprint' 值。

x509certMulti 是一个具有 2 个键的数组

  • signing。一个证书数组,将用于验证 IdP 签名
  • encryption 一个包含一个唯一证书的数组,将用于加密发送给 IdP 的数据

重放攻击

为了避免重放攻击,您可以存储已处理的 SAML 消息的 ID,以避免重复处理。由于消息会过期并且由于该原因将无效,因此您不需要存储这些 ID 超过当前接受的时间范围。

使用 Auth 对象的 getLastMessageId/getLastAssertionId 方法获取最后处理的消息/断言的 ID。

主要类和方法

下面描述了可以调用的主要类和方法。

Saml2 库

现在让我们描述 SAML2 库的类和方法。

OneLogin\Saml2\Auth - Auth.php

OneLogin PHP 工具包的主类

  • Auth - 初始化 SP SAML 实例
  • login - 启动 SSO 流程。
  • logout - 启动 SLO 流程。
  • processResponse - 处理 IdP 发送的 SAML 响应。
  • processSLO - 处理 IdP 发送的 SAML 登出响应/退出请求。
  • redirectTo - 将用户重定向到通过参数传入的 URL 或我们定义的 SSO 请求中的 URL。
  • isAuthenticated - 检查用户是否已认证。
  • getAttributes - 返回 SAML 属性集。
  • getAttribute - 返回请求的 SAML 属性。
  • getNameId - 返回 nameID。
  • getNameIdFormat - 获取来自 IdP 的 SAML 响应提供的 NameID 格式。
  • getNameIdNameQualifier - 获取来自 SAML 响应字符串的 NameID NameQualifier。
  • getNameIdSPNameQualifier - 获取来自 SAML 响应字符串的 NameID SP NameQualifier。
  • getSessionIndex - 获取 AuthnStatement 中的 SessionIndex。
  • getErrors - 返回是否存在错误。
  • getSSOurl - 获取 SSO url。
  • getSLOurl - 获取 SLO url。
  • getLastRequestID - 返回最后生成的 Request SAML 消息的 ID。
  • buildRequestSignature - 为 SAML 请求生成签名。
  • buildResponseSignature - 为 SAML 响应生成签名。
  • getSettings - 返回设置信息。
  • setStrict - 设置严格模式启用/禁用。
  • getLastRequestID - 获取由服务提供商生成的最后 AuthNRequest 或 LogoutRequest 的 ID。
  • getLastRequestXML - 返回最近构建/处理的 XML SAML 请求(AuthNRequest,LogoutRequest)。
  • getLastResponseXML - 返回最近构建/处理的 XML SAML 响应(SAMLResponse,LogoutResponse)。如果 SAMLResponse 包含加密断言,则解密它。
OneLogin\Saml2\AuthnRequest - AuthnRequest.php

SAML 2 认证请求类

  • AuthnRequest - 构建 AuthnRequest 对象。
  • getRequest - 返回压缩、base64 编码、未签名的 AuthnRequest
  • getId - 返回 AuthNRequest ID。
  • getXML - 返回请求中将发送的 XML。
OneLogin\Saml2\Response - Response.php

SAML 2 认证响应类

  • Response - 构建 SAML 响应对象。
  • isValid - 使用证书确定 SAML 响应是否有效。
  • checkStatus - 检查状态是否成功。
  • getAudiences - 获取受众。
  • getIssuers - 获取发行者(从响应和断言中获取)。
  • getNameIdData - 获取 IdP 提供的 NameID 数据。
  • getNameId - 获取 IdP 提供的 NameID。
  • getNameIdFormat - 获取来自 IdP 的 SAML 响应提供的 NameID 格式。
  • getNameIdNameQualifier - 获取来自 SAML 响应字符串的 NameID NameQualifier。
  • getNameIdSPNameQualifier - 获取来自 SAML 响应字符串的 NameID SP NameQualifier。
  • getSessionNotOnOrAfter - 从 AuthnStatement 获取 SessionNotOnOrAfter。
  • getSessionIndex - 获取 AuthnStatement 中的 SessionIndex。
  • getAttributes - 从 AttributeStatement 元素获取属性。
  • validateNumAssertions - 验证文档只包含单个断言(加密或不加密)。
  • validateTimestamps - 验证根据条件元素文档是否仍然有效。
  • getError - 执行验证过程后,如果失败,则此方法返回原因。
  • getXMLDocument - 返回 SAML 响应文档(如果包含加密断言,则解密它)。
OneLogin\Saml2\LogoutRequest - LogoutRequest.php

SAML 2 注销请求类

  • LogoutRequest - 构建 Logout 请求对象。
  • getRequest - 返回注销请求的压缩、base64 编码、未签名的数据。
  • getID - 返回注销请求的 ID。 (如果您有对象,您可以访问 id 属性)
  • getNameIdData - 获取注销请求的 NameID 数据。
  • getNameId - 获取注销请求的 NameID。
  • getIssuer - 获取注销请求的发行者。
  • getSessionIndexes - 从注销请求中获取 SessionIndexes。
  • isValid - 检查接收到的注销请求是否有效。
  • getError - 执行验证过程后,如果失败,则此方法返回原因。
  • getXML - 返回请求中将发送的 XML 或在 SP 中接收到的 XML。
OneLogin\Saml2\LogoutResponse - LogoutResponse.php

SAML 2 注销响应类

  • LogoutResponse - 构建一个注销响应对象(从设置中初始化参数,如果提供则加载注销响应)。
  • getIssuer - 获取注销响应的发行者。
  • getStatus - 获取注销响应的状态。
  • isValid - 判断SAML登出响应是否有效
  • build - 生成登出响应对象。
  • getResponse - 返回登出响应对象。
  • getError - 在执行验证过程后,如果失败,此方法返回原因。
  • getXML - 返回作为响应部分发送的XML或SP收到的XML。
OneLogin\Saml2\Settings - Settings.php

OneLogin PHP工具包的配置

  • Settings - 初始化设置:设置不同文件夹的路径,并从设置文件或提供的数组/对象中加载设置信息
  • checkSettings - 检查设置信息。
  • getBasePath - 返回基本路径。
  • getCertPath - 返回证书路径。
  • getLibPath - 返回库路径。
  • getExtLibPath - 返回外部库路径。
  • getSchemasPath - 返回模式路径。
  • checkSPCerts - 检查SP的x509证书是否存在且有效。
  • getSPkey - 返回SP的x509私钥。
  • getSPcert - 返回SP的x509公钥证书。
  • getSPcertNew - 返回SP未来的x509公钥证书。
  • getIdPData - 获取IdP数据。
  • getSPData - 获取SP数据。
  • getSecurityData - 获取安全数据。
  • getContacts - 获取联系数据。
  • getOrganization - 获取组织数据。
  • getSPMetadata - 获取SP元数据。XML表示形式。
  • validateMetadata - 验证XML SP元数据。
  • formatIdPCert - 格式化IdP证书。
  • formatSPCert - 格式化SP证书。
  • formatSPCertNew - 格式化新的SP证书。
  • formatSPKey - 格式化SP私钥。
  • getErrors - 返回包含错误的数组,当设置正确时数组为空。
  • getLastErrorReason - 返回最后一个错误的原因。
  • getBaseURL - 如果有设置,返回设置的baseurl。
  • setBaseURL - 设置baseurl值。
  • setStrict - 启用或禁用严格模式。
  • isStrict - 返回是否启用了“严格”模式。
  • isDebugActive - 返回是否启用了调试。
OneLogin\Saml2\Metadata - Metadata.php

一个包含与SP元数据相关的功能的类

  • builder - 根据设置生成SP的元数据。
  • signmetadata - 使用提供的密钥/证书签名元数据。
  • addX509KeyDescriptors - 将x509描述符(签名/加密)添加到元数据中。
OneLogin\Saml2\Utils - Utils.php

一个包含多个方法的辅助类

  • validateXML - 此函数尝试将XML字符串与指定的模式进行验证。
  • formatCert - 返回一个x509证书(如果需要,添加头部和尾部)。
  • formatPrivateKey - 返回一个RSA私钥(如果需要,添加头部和尾部)。
  • redirect - 执行到提供的url的重定向(或返回目标url)。
  • isHTTPS - 检查是否为https或http。
  • getSelfHost - 返回当前主机。
  • getSelfURLhost - 返回协议 + 当前主机 + 端口(如果与常用端口不同)。
  • getSelfURLNoQuery - 返回当前主机 + 当前视图的URL。
  • getSelfURL - 返回当前主机 + 当前视图 + 查询的URL。
  • generateUniqueID - 生成一个唯一的字符串(例如,用作断言的ID)。
  • parseTime2SAML - 将UNIX时间戳转换为SAML2时间戳,格式为yyyy-mm-ddThh:mm:ss(\.s+)?Z
  • parseSAML2Time - 将格式为yyyy-mm-ddThh:mm:ss(\.s+)?Z的SAML2时间戳转换为UNIX时间戳。忽略微秒部分。
  • parseDuration - 解释相对于给定时间戳的ISO8601持续时间值。
  • getExpireTime - 比较两个日期并返回最早的日期。
  • query - 从DOMDocument中提取节点。
  • isSessionStarted - 检查会话是否已启动。
  • deleteLocalSession - 删除本地会话。
  • calculateX509Fingerprint - 计算x509证书的指纹。
  • formatFingerPrint - 格式化指纹。
  • generateNameId - 生成nameID
  • getStatus - 从响应中获取状态。
  • decryptElement - 解密加密的元素。
  • castKey - 将XMLSecurityKey转换为正确的算法。
  • addSign - 将签名密钥和发送者证书添加到元素(消息或断言)中。
  • validateSign - 验证签名(消息或断言)。
OneLogin\Saml2\IdPMetadataParser - IdPMetadataParser.php

辅助类,包含检索和处理IdP元数据的方法。

  • parseRemoteXML - 从URL获取IdP元数据信息。
  • parseFileXML - 从文件获取IdP元数据信息。
  • parseXML - 从XML获取IdP元数据信息。
  • injectIntoSettings - 将元数据信息注入php-saml设置数组。

该类不会以任何方式验证在像parseRemoteXML这样的方法中引入的URL,以检索远程XML。通常,处理服务提供者的管理员会设置属于可信第三方IdP的URL。但还有其他场景,比如SAAS应用程序,其中应用程序管理员将责任委托给其他管理员。在这种情况下,应采取额外保护来验证此类URL输入,以避免如SSRF之类的攻击。

有关更多信息,请参阅源代码;每个方法都有文档,并提供有关其功能和使用方法的详细信息。请确保还检查doc文件夹,其中提供了有关SAML和SAML2的类和方法的HTML文档。

工具包中包含的演示

该工具包包含三个演示应用程序,以教授如何使用工具包,请查看。

演示需要在使用之前正确配置SP和IdP。

Demo1

SP设置

Onelogin的PHP工具包允许您以两种方式提供设置信息

  • 使用应位于工具包基本文件夹中的settings.php文件。
  • 使用设置数据数组。

在此演示中,我们使用名为$settingsInfo的设置数组以第二种方式提供数据。此数组使用包含的模板settings_example.php来创建settings.php设置并将其存储在demo1/文件夹中。配置SP部分,然后检查IdP的元数据并完成IdP信息的输入。

如果您检查index.php文件的代码,您将看到加载了settings.php文件,以便使用$settingsInfo变量来初始化Setting类。

请注意,在此演示中,可以在工具包的基本文件夹中定义的setting.php文件被忽略,并且使用工具包基本文件夹中的_toolkit_loader.php加载库。

IdP设置

一旦配置了SP,SP的元数据就会发布在metadata.php文件中。根据该信息配置IdP。

工作原理

  1. 第一次访问index.php视图时,您可以选择登录并返回到同一视图或登录并重定向到attrs.php视图。

  2. 当您点击

    2.1 在第一个链接中,我们访问到 (index.php?sso),向身份提供者 (IdP) 发送一个 AuthNRequest。在 IdP 进行身份验证后,通过用户的客户端将响应发送到服务提供者 (SP),具体是断言消费者服务视图:index.php?acs。请注意,将 RelayState 参数设置为启动该过程的 URL,即 index.php 视图。

    2.2 在第二个链接中,我们访问到 (attrs.php),与 2.1 中描述的过程相同,区别在于将 RelayState 设置为 attrs.php

  3. 在 ACS (index.php?acs) 中处理 SAML 响应,如果响应无效,则在此处停止处理并显示消息。否则,我们将重定向到 RelayState 视图。a) index.php 或 b) attrs.php

  4. 在应用中登录并显示用户属性。此时,我们可以测试单点登出功能。

  5. 可以通过两种方式测试单点登出功能。

    5.1 由 SP 启动的 SLO。在 SP 上单击“注销”链接,之后向 IdP 发送注销请求,关闭 IdP 中的会话,并通过客户端向 SP 发送注销响应(发送到单点注销服务端点)。SP 的 SLS 端点 (index.php?sls) 处理注销响应,如果有效,则关闭本地应用的用户会话。请注意,SLO 工作流程在 SP 上开始和结束。

    5.2 由 IdP 启动的 SLO。在这种情况下,操作在 IdP 端进行,注销过程在 IdP 启动,向 SP 发送注销请求(SLS 端点,index.php?sls)。SP 的 SLS 端点处理注销请求,如果有效,则关闭本地应用的用户会话,并向 IdP 发送注销响应(发送到 IdP 的 SLS 端点)。IdP 接收注销响应,处理它并关闭 IdP 的会话。请注意,SLO 工作流程在 IdP 上开始和结束。

请注意,所有 SAML 请求和响应都由一个唯一的文件处理,即 index.php 文件,以及如何使用 GET 参数来确定必须执行的操作。

演示2

SP设置

Onelogin的PHP工具包允许您以两种方式提供设置信息

  • 使用应位于工具包基本文件夹中的settings.php文件。
  • 使用设置数据数组。

首先,这是演示 2 应用的案例。应在工具包的根目录中定义 setting.php 文件和 setting_extended.php 文件。查阅 setting_example.phpadvanced_settings_example.php 来了解如何构建它们。

在这种情况下,我们将使用位于端点文件夹中的文件(acs.phpsls.php)作为属性消费服务和单点注销服务。

IdP设置

一旦配置了 SP,SP 的元数据就发布在 metadata.php 文件中。基于这些信息,配置 IdP。

工作原理

在演示 1 中,我们看到了所有 SAML 请求和响应都在一个唯一的文件,即 index.php 文件中处理。这个演示 1 使用高级编程。

在演示 2 中,我们有几个视图:index.phpsso.phpslo.phpconsume.phpmetadata.php。正如我们所说的,我们将使用工具包中定义的端点(端点文件夹中的 acs.phpsls.php)。这个演示 2 使用低级编程。

请注意,SSO 操作可以在 index.phpsso.php 中启动。

在演示 1 和演示 2 中发生的 SAML 工作流程是相似的,只是改变了目标。

  1. 第一次访问 index.phpsso.php 时,会自动向 IdP 发送 AuthNRequest,(因为发送了原始 URL 的 RelayState)。我们在 IdP 进行身份验证,然后发送一个 Response 到 SP,到 ACS 端点,在这种情况下是端点文件夹中的 acs.php

  2. SAML响应在ACS中处理,如果Response无效,则处理停止,并显示消息。否则,我们将被重定向到RelayState视图(sso.phpindex.php)。sso.php检测用户是否已登录,并将其重定向到index.php,因此最终我们将位于index.php

  3. 我们已经登录到应用程序,并显示用户属性(如果有的话)。在这个时候,我们可以测试单点退出功能。

  4. 可以通过两种方式测试单点登出功能。

    4.1 SP发起的SLO。在SP上点击“退出”链接,之后我们将被重定向到slo.php视图,在那里向IdP发送退出请求,关闭IdP中的会话,并向SP回复退出响应(发送到单点退出服务端点)。在这种情况下,SP的SLS端点处理退出响应,如果有效,则关闭本地应用程序的用户会话。请注意,SLO工作流程从SP开始并结束。

    4.2 IdP发起的SLO。在这种情况下,动作在IdP端发生,退出过程在idP端启动,向SP(端点文件夹的sls.php端点)发送退出请求。SP的SLS端点处理退出请求,如果有效,则关闭本地应用程序中用户的会话,并向IdP(向IdP的SLS端点)发送退出响应。IdP收到退出响应,处理它并关闭IdP中的会话。请注意,SLO工作流程从IdP开始并结束。