adactive-sas/saml2-bridge-bundle
Symfony 扩展包,提供 SAML 身份提供者 (idp) 功能。
Requires
- php: >=5.6,<8.0-dev
- ext-openssl: *
- simplesamlphp/saml2: ^1.10.3
- symfony/symfony: ^2.7.0|^3.3.0|^4.0
- symfony/workflow: ^3.3.0|^4.0
Requires (Dev)
- liip/functional-test-bundle: ^1.7
- phpunit/phpunit: ^5.7.17
- satooshi/php-coveralls: ^2.0.0
- symfony/phpunit-bridge: ^2.8.0|^3.0.0
- twig/twig: ^1.34|^2.0
This package is not auto-updated.
Last update: 2024-09-29 02:07:48 UTC
README
通过使用 simplesamlphp/saml2 并受 OpenConext/Stepup-saml-bundle 启发,为您的应用程序添加 SAML 功能的扩展包。
SAML 支持
SAML 支持有限,此扩展包可用于提供以下支持的基本身份提供者:
- 基本元数据
- 单点登录
- 绑定
- Http-POST & Http-Redirect 签名请求
- Http-POST & Http-Post 签名响应
- 绑定
- 单点登出
- 绑定
- Http-POST & Http-Redirect 签名请求
- Http-POST & Http-Redirect 签名响应
- 包括由身份提供者发起和由服务提供者发起的两种方式
- 绑定
入门
安装
-
将包添加到您的 Composer 文件中
composer require adactive-sas/saml2-bridge-bundle
-
将扩展包添加到您的内核中
app/AppKernel.php
public function registerBundles() { // ... $bundles[] = new AdactiveSas\Saml2BridgeBundle\AdactiveSasSaml2BridgeBundle(); }
配置
adactive_sas_saml2_bridge: hosted: metadata_route: name_of_the_route_of_metadata_url identity_provider: enabled: true service_provider_repository: service.name.of.entity_repository sso_route: name_of_the_route_of_the_single_sign_on_url sls_route: name_of_the_route_of_the_single_logout_url login_route: name_of_the_route_of_the_login_url logout_route: name_of_the_route_of_the_logout_url public_key: %idp_public_key_file_path% private_key: %idp_private_key_file_path%
还要添加登出处理程序。
logout: handlers: [adactive_sas_saml2_bridge.logout.handler]
托管配置列表了您的应用程序提供的服务的配置(SP、IdP 或两者)。可以通过各自的 enabled
标志单独开启和关闭 SP 和 IdP 功能。
最后一行的内联证书可以替换为包含该证书的文件系统路径的 certificate_file
。
建议使用上述列出的参数。各种 publickey
和 privatekey
变量是密钥的单行内容,没有证书等分隔符。强烈建议使用上述参数,以便将实际密钥内容保留在配置文件之外(例如使用本地 parameters.yml
文件)。
service_provider_repository
是您提供 IdP 服务的服务提供者的存储库。配置的服务必须实现 AdactiveSas\Saml2BridgeBundle\Entity\ServiceProviderRepository
接口。
示例用法
实现服务提供者存储库
<?php namespace Acme\SamlBundle\Entity; use AdactiveSas\Saml2BridgeBundle\Entity\ServiceProvider; use AdactiveSas\Saml2BridgeBundle\Entity\ServiceProviderRepository; class SamlServiceProviderRepository implements ServiceProviderRepository { protected $spMap = []; public function __construct() { $this->spMap["https://test.fake/metadata"] = new ServiceProvider( [ /** * Returns the contents of an X509 pem certificate, without the '-----BEGIN CERTIFICATE-----' and * '-----END CERTIFICATE-----'. * * @return null|string */ 'certificateData' => 'MIIEJTCCAw2gAwIBAgIJANug+o++1X5IMA0GCSqGSIb3DQEBCwUAMIGoMQswCQYDVQQGEwJOTDEQMA4GA1UECAwHVXRyZWNodDEQMA4GA1UEBwwHVXRyZWNodDEVMBMGA1UECgwMU1VSRm5ldCBCLlYuMRMwEQYDVQQLDApTVVJGY29uZXh0MRwwGgYDVQQDDBNTVVJGbmV0IERldmVsb3BtZW50MSswKQYJKoZIhvcNAQkBFhxzdXJmY29uZXh0LWJlaGVlckBzdXJmbmV0Lm5sMB4XDTE0MTAyMDEyMzkxMVoXDTE0MTExOTEyMzkxMVowgagxCzAJBgNVBAYTAk5MMRAwDgYDVQQIDAdVdHJlY2h0MRAwDgYDVQQHDAdVdHJlY2h0MRUwEwYDVQQKDAxTVVJGbmV0IEIuVi4xEzARBgNVBAsMClNVUkZjb25leHQxHDAaBgNVBAMME1NVUkZuZXQgRGV2ZWxvcG1lbnQxKzApBgkqhkiG9w0BCQEWHHN1cmZjb25leHQtYmVoZWVyQHN1cmZuZXQubmwwggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQDXuSSBeNJY3d4p060oNRSuAER5nLWT6AIVbv3XrXhcgSwc9m2b8u3ksp14pi8FbaNHAYW3MjlKgnLlopYIylzKD/6Ut/clEx67aO9Hpqsc0HmIP0It6q2bf5yUZ71E4CN2HtQceO5DsEYpe5M7D5i64kS2A7e2NYWVdA5Z01DqUpQGRBc+uMzOwyif6StBiMiLrZH3n2r5q5aVaXU4Vy5EE4VShv3Mp91sgXJj/v155fv0wShgl681v8yf2u2ZMb7NKnQRA4zM2Ng2EUAyy6PQ+Jbn+rALSm1YgiJdVuSlTLhvgwbiHGO2XgBi7bTHhlqSrJFK3Gs4zwIsop/XqQRBAgMBAAGjUDBOMB0GA1UdDgQWBBQCJmcoa/F7aM3jIFN7Bd4uzWRgzjAfBgNVHSMEGDAWgBQCJmcoa/F7aM3jIFN7Bd4uzWRgzjAMBgNVHRMEBTADAQH/MA0GCSqGSIb3DQEBCwUAA4IBAQBd80GpWKjp1J+Dgp0blVAox1s/WPWQlex9xrx1GEYbc5elp3svS+S82s7dFm2llHrrNOBt1HZVC+TdW4f+MR1xq8O5lOYjDRsosxZc/u9jVsYWYc3M9bQAx8VyJ8VGpcAK+fLqRNabYlqTnj/t9bzX8fS90sp8JsALV4g84Aj0G8RpYJokw+pJUmOpuxsZN5U84MmLPnVfmrnuCVh/HkiLNV2c8Pk8LSomg6q1M1dQUTsz/HVxcOhHLj/owwh3IzXf/KXV/E8vSYW8o4WWCAnruYOWdJMI4Z8NG1Mfv7zvb7U3FL1C/KLV04DqzALXGj+LVmxtDvuxqC042apoIDQV', /** * Returns the full path to the (local) file that contains the X509 pem certificate. * * @return null|string */ "certificateFile" => "", /** * @return null|string */ "entityId" => "https://test.fake/saml/metadata", /** * @return null|bool */ "assertionEncryptionEnabled" => true, "assertionConsumerUrl" => "https://test.fake/saml/acs", "assertionConsumerBinding" => \SAML2_Const::BINDING_HTTP_POST, "singleLogoutUrl" => "https://test.fake/saml/sls", "singleLogoutBinding" => \SAML2_Const::BINDING_HTTP_REDIRECT, "nameIdFormat" => \SAML2_Const::NAMEID_PERSISTENT, "nameIdValue" => function (UserInterface $user) { /** @var User $user */ return $user->getEmailCanonical(); }, "NameQualifier" => 'test.fake', "wantSignedAuthnRequest" => true, "wantSignedAuthnResponse" => true, "wantSignedAssertions" => false, "wantSignedLogoutRequest" => false, "wantSignedLogoutResponse" => false, "attributes" => [ 'User.Email' => function (UserInterface $user) { /** @var User $user */ return $user->getEmailCanonical(); }, 'User.Username' => function (UserInterface $user) { /** @var User $user */ return $user->getName(); }, 'first_name' => function (UserInterface $user) { /** @var User $user */ return $user->getFirstName(); }, 'last_name' => function (UserInterface $user) { /** @var User $user */ return $user->getLastName(); }, ], "validAudiences" => [ "https://test.fake/saml/acs", ], "assertionNotBeforeInterval" => new \DateInterval('PT0S'), "assertionNotOnOrAfterInterval" => new \DateInterval('PT5M'), "assertionSessionNotOnOrAfterInterval" => new \DateInterval('P1D'), ] ); } /** * @param string $entityId * @return ServiceProvider */ public function getServiceProvider($entityId) { return $this->hasServiceProvider($entityId) ? $this->spMap[$entityId] : null; } /** * @param string $entityId * @return bool */ public function hasServiceProvider($entityId) { return array_key_exists($entityId, $this->spMap); } }
Slack 示例
<?php $this->spMap["https://slack.com"] = new ServiceProvider( [ /** * Returns the contents of an X509 pem certificate, without the '-----BEGIN CERTIFICATE-----' and * '-----END CERTIFICATE-----'. * * @return null|string */ 'certificateData' => 'MIIDrzCCApagAwIBAgIBADANBgkqhkiG9w0BAQ0FADBxMQswCQYDVQQGEwJ1czETMBEGA1UECAwKQ2FsaWZvcm5pYTEhMB8GA1UECgwYU2xhY2sgVGVjaG5vbG9naWVzLCBJbmMuMRIwEAYDVQQDDAlzbGFjay5jb20xFjAUBgNVBAcMDVNhbiBGcmFuY2lzY28wHhcNMTUwMzE3MDEyMzMyWhcNMjUwMzE0MDEyMzMyWjBxMQswCQYDVQQGEwJ1czETMBEGA1UECAwKQ2FsaWZvcm5pYTEhMB8GA1UECgwYU2xhY2sgVGVjaG5vbG9naWVzLCBJbmMuMRIwEAYDVQQDDAlzbGFjay5jb20xFjAUBgNVBAcMDVNhbiBGcmFuY2lzY28wggEjMA0GCSqGSIb3DQEBAQUAA4IBEAAwggELAoIBAgDB0y4ruySosz1GX/3KI1jp4oivxtnXLeMwKELrBgG+rZ8pl+UMhLG2iCp0nbnwSxXVU0ONJVI3SSzJ5VQtBHHCA4UAzse0HRaSZfBs+6urKoMLf8iusBYk62f2g/RAPjsMVcjC8B3FHyhaD9OnWSdJ7uGopmwwEhDiwf/gdS9Uw8FojYDuVprODfmj7+fgWPkGTf8TRGaHjudjuP1LMDRAz2cI0ym09jbnW8BVynSjjUrE+K9ri1uWzT2tp49OHqSgjaXkWWY6prFa9MT8jsibe02Id2i5+h0c4F892O7MybNWgF139dMGapmW4rf3GT7brLZEO4sZPwovhlj3b6U+8wIDAQABo1AwTjAdBgNVHQ4EFgQUa2YVk5yi+WMxLT/q7rokAfzyvU0wHwYDVR0jBBgwFoAUa2YVk5yi+WMxLT/q7rokAfzyvU0wDAYDVR0TBAUwAwEB/zANBgkqhkiG9w0BAQ0FAAOCAQIAUwv53vh2LkgbJbBGyRlSkjAZyybwM7pO6TtQ4SHyn366SG1lZXkc9S9u8m4kMETDquOujC/fZLiAe4f8rZ8+ZXV0f17FL/RhMDzVBv6DgDabfpXAkt+Yn+ZIThFi2D7L4jyJzZPbaf7soCu1e/Dx0CBhm/Lz2nsny6Il7rkEbDB7gBpjZODMMi/PEJ5I462JUrj+9aSZBtx2/NXIoFkLZ1B4j3UG+WJhcYMlMBim/GTimKS7yzkvfqdADmIAaO0RPYduNPds6Dyjyjbqj3XR3WwdsmTorO95UKitRGu10ImwByXo2xzQCwGNP8WuRAmWVIlisLLNEDKTnZDb38085gY=', /** * Returns the full path to the (local) file that contains the X509 pem certificate. * * @return null|string */ "certificateFile" => "", /** * @return null|string */ "entityId" => "https://slack.com", /** * @return null|bool */ "assertionEncryptionEnabled" => true, "assertionConsumerUrl" => "https://$slackTeamName.slack.com/sso/saml", "assertionConsumerBinding" => \SAML2_Const::BINDING_HTTP_POST, "singleLogoutUrl" => "https://$slackTeamName.slack.com/sso/saml/logout", "singleLogoutBinding" => \SAML2_Const::BINDING_HTTP_REDIRECT, "nameIdFormat" => \SAML2_Const::NAMEID_PERSISTENT, "nameIdValue" => function (UserInterface $user) { /** @var User $user */ return $user->getEmailCanonical(); }, "NameQualifier" => "$slackTeamName.slack.com", "wantSignedAuthnRequest" => true, "wantSignedAuthnResponse" => true, "wantSignedAssertions" => false, "attributes" => [ 'User.Email' => function (UserInterface $user) { /** @var User $user */ return $user->getEmailCanonical(); }, 'User.Username' => function (UserInterface $user) { /** @var User $user */ return $user->getName(); }, 'first_name' => function (UserInterface $user) { /** @var User $user */ return $user->getFirstName(); }, 'last_name' => function (UserInterface $user) { /** @var User $user */ return $user->getLastName(); }, ], ] );
Freshdesk 示例
<?php $this->spMap["https://$freshdeskAccountName.freshdesk.com"] = new ServiceProvider( [ /** * Returns the contents of an X509 pem certificate, without the '-----BEGIN CERTIFICATE-----' and * '-----END CERTIFICATE-----'. * * @return null|string */ 'certificateData' => '', /** * Returns the full path to the (local) file that contains the X509 pem certificate. * * @return null|string */ "certificateFile" => "", /** * @return null|string */ "entityId" => "https://$freshdeskAccountName.freshdesk.com", /** * @return null|bool */ "assertionEncryptionEnabled" => false, "assertionConsumerUrl" => "https://$freshdeskAccountName.freshdesk.com/login/saml", "assertionConsumerBinding" => \SAML2_Const::BINDING_HTTP_POST, "singleLogoutUrl" => "https://$freshdeskAccountName.freshdesk.com/logout/saml", "singleLogoutBinding" => \SAML2_Const::BINDING_HTTP_REDIRECT, "nameIdFormat" => 'urn:oasis:names:tc:SAML:2.0:nameid-format:email', "nameIdValue" => function (UserInterface $user) { /** @var User $user */ return $user->getEmailCanonical(); }, "NameQualifier" => "$freshdeskAccountName.freshdesk.com", "wantSignedAuthnRequest" => false, "wantSignedAuthnResponse" => false, "wantSignedAssertions" => true, "attributes" => [ 'email' => function (UserInterface $user) { /** @var User $user */ return $user->getEmailCanonical(); }, 'name' => function (UserInterface $user) { /** @var User $user */ return $user->getName(); }, 'given_name' => function (UserInterface $user) { /** @var User $user */ return $user->getFirstName(); }, 'family_name' => function (UserInterface $user) { /** @var User $user */ return $user->getLastName(); }, ], ] );
NewRelic 示例
<?php $this->spMap["rpm.newrelic.com"] = new ServiceProvider( [ /** * Returns the contents of an X509 pem certificate, without the '-----BEGIN CERTIFICATE-----' and * '-----END CERTIFICATE-----'. * * @return null|string */ 'certificateData' => '', /** * Returns the full path to the (local) file that contains the X509 pem certificate. * * @return null|string */ "certificateFile" => "", /** * @return null|string */ "entityId" => "rpm.newrelic.com", /** * @return null|bool */ "assertionEncryptionEnabled" => false, "assertionConsumerUrl" => "https://rpm.newrelic.com/accounts/$accountId/sso/saml/finalize", "assertionConsumerBinding" => \SAML2_Const::BINDING_HTTP_POST, "singleLogoutUrl" => "", "singleLogoutBinding" => \SAML2_Const::BINDING_HTTP_REDIRECT, "nameIdFormat" => 'urn:oasis:names:tc:SAML:1.1:nameid-format:emailAddress', "nameIdValue" => function (UserInterface $user) { /** @var User $user */ return $user->getEmailCanonical(); }, "NameQualifier" => "rpm.newrelic.com", "wantSignedAuthnRequest" => false, "wantSignedAuthnResponse" => false, "wantSignedAssertions" => true, "attributes" => [], ] );
注意:请记住这是一个示例,您可能需要从数据库中检索 ServiceProviders
创建控制器
<?php namespace Acme\SamlBundle\Controller; use Symfony\Bundle\FrameworkBundle\Controller\Controller; use Symfony\Component\HttpFoundation\Request; use Symfony\Component\Routing\Annotation\Route; /** * @Route("/saml") */ class SamlController extends Controller { /** * @Route("/sso", name="acme_saml_sso") * @return \Symfony\Component\HttpFoundation\Response */ public function singleSignOnAction(Request $httpRequest) { $idpProcessor = $this->get("adactive_sas_saml2_bridge.processor.hosted_idp"); return $idpProcessor->processSingleSignOn($httpRequest); } /** * @Route("/sls", name="acme_saml_sls") * @return \Symfony\Component\HttpFoundation\Response */ public function singleLogoutAction(Request $httpRequest) { $idpProcessor = $this->get("adactive_sas_saml2_bridge.processor.hosted_idp"); return $idpProcessor->processSingleLogoutService($httpRequest); } /** * @Route("/metadata", name="acme_saml_metadata", defaults={"_format"="xml"}) * * @return \Symfony\Component\HttpFoundation\Response */ public function metadataAction() { $idpProcessor = $this->get("adactive_sas_saml2_bridge.processor.hosted_idp"); return $idpProcessor->getMetadataXmlResponse(); } }
定义服务
<?xml version="1.0" ?> <container xmlns="https://symfony.com.cn/schema/dic/services" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="https://symfony.com.cn/schema/dic/services https://symfony.com.cn/schema/dic/services/services-1.0.xsd"> <services> <service id="acme.saml.service_provider_repository" class="Acme\SamlBundle\Entity\SamlServiceProviderRepository"> </service> </services> </container>
配置
adactive_sas_saml2_bridge: hosted: metadata_route: acme_saml_metadata identity_provider: enabled: true service_provider_repository: acme.saml.service_provider_repository sso_route: acme_saml_sso sls_route: acme_saml_sls login_route: fos_user_security_login logout_route: fos_user_security_logout public_key: '%kernel.root_dir%/../vendor/adactive-sas/saml2-bridge-bundle/src/Resources/keys/development_publickey.cer' private_key: '%kernel.root_dir%/../vendor/adactive-sas/saml2-bridge-bundle/src/Resources/keys/development_privatekey.pem'
注意:这是开发密钥,绝不要在生产环境中使用它们!
测试
我们知道这个扩展包确实缺少测试,这将在未来的版本中提供。
贡献
目前,这个扩展包非常有限,但设计为支持所有 SAML2 流程。
因此,请随意创建问题并提交拉取请求,以帮助我们使这个扩展包更完整。