Laravel Socialite 的 SAML2 服务提供者

4.7.2 2023-11-07 22:07 UTC

This package is auto-updated.

Last update: 2024-08-26 21:39:52 UTC


README

composer require socialiteproviders/saml2

安装与基本使用

请参阅基础安装指南,然后按照以下提供者的具体说明操作。

注意SAML 协议部分。

将配置添加到config/services.php

可以使用以下任何一种方法配置身份提供者。如果您的 IDP 支持它,强烈建议使用元数据 URL,这样在 IDP 端的证书轮换就不会导致任何服务中断。

使用身份提供者元数据 URL

'saml2' => [
  'metadata' => 'https://idp.co/metadata/xml',
],

使用身份提供者元数据 XML 文件

'saml2' => [
  'metadata' => file_get_contents('/path/to/metadata/xml'),
],

提供者将自动选择元数据中的第一个 IdP 描述符。如果您的元数据包含多个描述符,您可以通过同时使用 metadataentityid 配置选项来选择要使用的一个。

使用身份提供者元数据 URL,选择特定的描述符

'saml2' => [
  'metadata' => 'https://idp.co/metadata/xml',
  'entityid' => 'http://saml.to/trust',
],

使用证书字符串手动配置身份提供者

'saml2' => [
  'acs' => 'https://idp.co/auth/acs', // (the IDP's 'Assertion Consumer Service' URL. Also known as the assertion callback URL or SAML assertion consumer endpoint)
  'entityid' => 'http://saml.to/trust', // (the IDP's globally unique "Entity ID", normally formatted as a URI, but it is not a real URL)
  'certificate' => 'MIIC4jCCAcqgAwIBAgIQbDO5YO....', // (the IDP's assertion signing certificate)
],

使用证书文件手动配置身份提供者

'saml2' => [
  'acs' => 'https://idp.co/auth/acs',
  'entityid' => 'http://saml.to/trust',
  'certificate' => file_get_contents('/path/to/certificate.pem'),
],

添加提供者事件监听器

Laravel 11+

在 Laravel 11 中,默认的 EventServiceProvider 提供者已被移除。取而代之,您可以在 AppServiceProviderboot 方法中使用 Event 门面上的 listen 方法添加监听器。

  • 注意:除非您用自己的提供者覆盖它们,否则您不需要为内置的 Socialite 提供者添加任何内容。
Event::listen(function (\SocialiteProviders\Manager\SocialiteWasCalled $event) {
    $event->extendSocialite('saml2', \SocialiteProviders\Saml2\Provider::class);
});
Laravel 10 或以下配置包的监听器以监听 `SocialiteWasCalled` 事件。

app/Providers/EventServiceProvider 中的 listen[] 数组中添加该事件。有关详细说明,请参阅基础安装指南

protected $listen = [
    \SocialiteProviders\Manager\SocialiteWasCalled::class => [
        // ... other providers
        \SocialiteProviders\Saml2\Saml2ExtendSocialite::class.'@handle',
    ],
];

使用方法

现在您应该能够像常规使用 Socialite 一样使用提供者(假设您已安装门面)

要启动身份验证流程

Route::get('/auth/redirect', function () {
    return Socialite::driver('saml2')->redirect();
});

要接收回调

Route::get('/auth/callback', function () {
    $user = Socialite::driver('saml2')->user();
});

SAML 协议

为了与 Socialite 和 Laravel 充分兼容,Saml2 提供者默认使用 GET 路由进行身份验证回调。或者在 SAML 术语中,它使用服务提供者断言消费者 URL 上的 HTTP-Redirect 绑定

Route::get('/auth/callback', function () {
    $user = Socialite::driver('saml2')->user();
});

虽然这与 Socialite 的操作方式一致,但这并不是最常见的 SAML 回调风格,许多身份提供者都不支持它。正常方法是使用 HTTP-POST 绑定,Saml2 也支持它。要使用此方法,只需将您的 Laravel 路由定义为 POST 路由即可

Route::post('/auth/callback', function () {
    $user = Socialite::driver('saml2')->user();
});

但是,请注意,这与 Laravel 默认在 routes/web.php 文件上执行的 POST 路由的 CSRF 过滤不兼容。为了使此回调风格工作,您可以将此路由定义为 web.php 之外的路由,或者将其添加到您的 VerifyCsrfToken HTTP 中间件的异常中。

如果您添加了两个路由来支持两种绑定方法,您可以在 config/services.php 中像这样选择默认的一个

'saml2' => [
  'sp_default_binding_method' => \LightSaml\SamlConstants::BINDING_SAML2_HTTP_POST,
],

无状态

提供者支持 SAML2 无请求/身份提供者发起的请求。要使用此技术,必须将 回调 路由设置为无状态的。

Route::get('/auth/callback', function () {
    $user = Socialite::driver('saml2')->stateless()->user();
});

(注意,这与 标准 Socialite 使用方式不同,其中标记为无状态的 重定向

单点登出

警告!请注意,SAML2单点登出功能是集中登出的最佳尝试方式。根据当前情况,它需要特殊条件才能工作。您必须将您的会话配置设置为same_site = 'none'secure = true才能使其正常工作,这具有严重的安全影响。在使用此功能之前,请务必理解风险。

您可以通过添加一个GET路由来启用您的服务提供者上的SingleLogoutService,在该路由中,您将登出用户并生成SAML2登出响应

Route::get('/auth/saml2/logout', function () {
    $response = Socialite::driver('saml2')->logoutResponse();
});

要发布您的服务提供者元数据中的SingleLogoutService,您还必须在config/services.php中配置路由

'saml2' => [
  'sp_sls' => 'auth/saml2/logout',
],

签名和加密

SAML2支持消息和声明的签名和加密。许多身份提供者要求其中之一或两者都是必需的。要启用此功能,您可以为您的应用程序生成一个证书,并在config/services.php中提供它

'saml2' => [
  'sp_certificate' => file_get_contents('path/to/sp_saml.crt'),
  'sp_private_key' => file_get_contents('path/to/sp_saml.pem'),
  'sp_private_key_passphrase' => 'passphrase to your private key, provide it only if you have one',
  'sp_sign_assertions' => true, // or false to disable assertion signing
],

sp_private_key_passphrase是可选的,如果私钥未加密,则不应提供。

始终保护您的私钥,并将其存储在公众无法访问的地方。

使用openssl生成证书和私钥的示例命令

openssl req -x509 -sha256 -nodes -days 365 -newkey rsa:2048 -keyout sp_saml.pem -out sp_saml.crt

验证

提供者验证声明中的时间戳,包括NotBeforeNotOnOrAfter。默认的时钟偏移量是120秒,但这可以作为配置的一部分进行更改

'saml2' => [
  'metadata' => 'https://idp.co/metadata/xml',
  'validation' => [
    'clock_skew' => 30, // Time in seconds
  ],
],

提供者检查身份提供者永远不会重复一个断言ID。默认情况下,ID会永久记住,但这可以进行配置

'saml2' => [
  'metadata' => 'https://idp.co/metadata/xml',
  'validation' => [
    'repeated_id_ttl' => 365 * 24 * 60 * 60, // Time in seconds, or null to cache forever
  ],
],

身份提供者元数据

当使用身份提供者的元数据URL时,默认情况下,获取的元数据会缓存24小时。要修改此存活时间值,请使用config/services.php中的'ttl'键

'saml2' => [
  'metadata' => 'https://idp.co/metadata/xml',
  'ttl' => 3600, // TTL in seconds
],

要程序化地清除缓存,您可以使用

Socialite::driver('saml2')->clearIdentityProviderMetadataCache();

元数据每24小时重新获取一次,但如果获取失败,则以前获取的元数据将用于接下来的24小时。如果元数据的第一次获取失败,将抛出GuzzleException

服务提供者元数据

为了简化在身份提供者端配置Laravel服务提供者的配置,您可以在路由上公开服务提供者的XML元数据

Route::get('/auth/saml2/metadata', function () {
    return Socialite::driver('saml2')->getServiceProviderMetadata();
});

请注意,您的Laravel服务的断言消费者服务URL已填充在元数据中,因此必须在config/services.php的sp_acs键中设置,如果它不是Socialite的默认值'/auth/callback'。

例如,如果这是您的回调路由

Route::get('/auth/saml2/callback', function () {
    $user = Socialite::driver('saml2')->user();
});

则在config/services.php中应配置ACS路由

'saml2' => [
  'metadata' => 'https://idp.co/metadata/xml',
  'sp_acs' => 'auth/saml2/callback',
],

服务提供者的默认实体ID是到'/auth/saml2'(例如 https://your.domain.com/auth/saml2)的URL,如果需要,可以在config/services.php中手动配置,如下所示

'saml2' => [
  'metadata' => 'https://idp.co/metadata/xml',
  'sp_entityid' => 'https://my.domain.com/my/custom/entityid',
],

服务提供者的实体ID和断言消费者URL也可以通过以下方式程序化检索

Socialite::driver('saml2')->getServiceProviderEntityId()
Socialite::driver('saml2')->getServiceProviderAssertionConsumerUrl()

您还可以通过在config/services.php中配置它们来在元数据中发布服务提供者的组织和技术支持联系人

'saml2' => [
  'sp_tech_contact_surname' => 'Doe',
  'sp_tech_contact_givenname' => 'John',
  'sp_tech_contact_email' => 'john.doe@example.com',
  'sp_org_lang' => 'en',
  'sp_org_name' => 'Example Corporation Ltd.',
  'sp_org_display_name' => 'Example Corporation',
  'sp_org_url' => 'https://corp.example',
],

如果您想包含此信息,您必须至少配置组织包含的sp_org_name,以及联系人包含的sp_tech_contact_email。默认情况下,sp_org_lang为英文(en)。

当配置服务提供者证书时,签名和加密证书会自动包含在元数据中。

用户属性和Name ID

根据SAML惯例,由身份提供者发送的"Name ID"用作回调中返回的User类实例的ID。

来自 'http://schemas.xmlsoap.org/...' 和 'urn:oid:...' 命名空间的已知SAML属性映射到User类中的nameemailfirst_namelast_nameupn

由身份提供者返回的所有其他属性存储在User类的"raw"属性中,可以通过$user->getRaw()获取。

可以通过在config/services.php中提供一个部分/完整的自定义映射来扩展/覆盖默认映射。

'saml2' => [
  'attribute_map' => [
    // Add mappings as 'mapped_name' => 'saml_attribute' or 'mapped_name' => ['saml_attribute', ...], for example:
    'email' => [
      \SocialiteProviders\Saml2\OasisAttributeNameUris::MAIL,
      \LightSaml\ClaimTypes::EMAIL_ADDRESS,
    ],
    'phone' => \SocialiteProviders\Saml2\OasisAttributeNameUris::PHONE,
  ],
],

整个断言也存储在User实例中,可以通过$user->getAssertion()获取。