Symfony2 中的 saml2 身份提供者
saml2 identity provider in Symfony2
我必须实施 SAML2 身份提供程序 (IdP) 并将其与现有的 Symfony 2 应用程序集成。
我发现了一些实现服务提供者 (SP) 而不是身份提供者的捆绑包,所以我认为我可以使用 SimpleSAMLphp library。还有其他解决方案吗?
如何将我的用户提供商逻辑与 SimpleSAMLphp 集成?
我发现:
- SURFnet SamlBundle 这是您找到的库的简单 symfony2 包装器。
- SamlSpBundle 更常用且有据可查。
看看这两个。第一个非常简单,我不知道是否有足够的记录,当然是一个活跃的项目。第二种似乎更强大且有文档记录,但可能更难配置。
希望对您有所帮助
更新
正如 Milos Tomic 在他的评论中提到的,aerialship/lightsaml 被 lightsaml/sp-bundle. You can find an introduction here 取代。
++++++++++++++++++++++++++++
我最近使用 Simplesamlphp as IDP and SamlSPBundle 作为 SP 设置了一个 SAML 解决方案,一切正常。
我建议先安装 Simplesamlphp,然后再安装 good Documentation here。
一旦你启动了 IDP 并且 运行,你应该会看到一个欢迎页面和一个名为 Federation 的选项卡(或类似的东西,我的安装是德语的)。在那里你应该看到一个选项"SAML 2.0 IdP Metadata"。按照 link 并将显示的 XML 复制到一个单独的文件并保存该文件。
在 symfony 方面,我创建了一个新的 Bundle 并将其命名为 "SamlBundle"。按照其文档(第 1 步和第 2 步)中的说明下载并安装 SamlSPBundle。
创建您的 SSO State/User class(第 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;
将您的 class 添加到 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 步之前,我建议先创建您的用户提供程序 class。这是一个例子:
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 to simpleSAMLphp Metadata converter" Link。按照 link 并将您的 SP XML 数据转换为 simpleSAMLphp 格式。将该数据添加到 IDP 元数据文件夹中的 saml20-sp-remote.php 文件。
好的,我很确定我忘记了什么,但希望这些信息对您有所帮助。如果您遇到困难,欢迎您回复我。
我必须实施 SAML2 身份提供程序 (IdP) 并将其与现有的 Symfony 2 应用程序集成。
我发现了一些实现服务提供者 (SP) 而不是身份提供者的捆绑包,所以我认为我可以使用 SimpleSAMLphp library。还有其他解决方案吗?
如何将我的用户提供商逻辑与 SimpleSAMLphp 集成?
我发现:
- SURFnet SamlBundle 这是您找到的库的简单 symfony2 包装器。
- SamlSpBundle 更常用且有据可查。
看看这两个。第一个非常简单,我不知道是否有足够的记录,当然是一个活跃的项目。第二种似乎更强大且有文档记录,但可能更难配置。
希望对您有所帮助
更新
正如 Milos Tomic 在他的评论中提到的,aerialship/lightsaml 被 lightsaml/sp-bundle. You can find an introduction here 取代。
++++++++++++++++++++++++++++
我最近使用 Simplesamlphp as IDP and SamlSPBundle 作为 SP 设置了一个 SAML 解决方案,一切正常。
我建议先安装 Simplesamlphp,然后再安装 good Documentation here。
一旦你启动了 IDP 并且 运行,你应该会看到一个欢迎页面和一个名为 Federation 的选项卡(或类似的东西,我的安装是德语的)。在那里你应该看到一个选项"SAML 2.0 IdP Metadata"。按照 link 并将显示的 XML 复制到一个单独的文件并保存该文件。
在 symfony 方面,我创建了一个新的 Bundle 并将其命名为 "SamlBundle"。按照其文档(第 1 步和第 2 步)中的说明下载并安装 SamlSPBundle。
创建您的 SSO State/User class(第 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;
将您的 class 添加到 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 步之前,我建议先创建您的用户提供程序 class。这是一个例子:
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 to simpleSAMLphp Metadata converter" Link。按照 link 并将您的 SP XML 数据转换为 simpleSAMLphp 格式。将该数据添加到 IDP 元数据文件夹中的 saml20-sp-remote.php 文件。
好的,我很确定我忘记了什么,但希望这些信息对您有所帮助。如果您遇到困难,欢迎您回复我。