nirajp / laravel-saml2
一个用于多个身份提供者(IDP)的Laravel包,作为SP(服务提供者)进行Saml2集成。从aacotroneo/laravel-saml2分支而来。
Requires
- php: >=5.4.0
- illuminate/support: >=5.0.0
- onelogin/php-saml: 2.9.1
Requires (Dev)
- mockery/mockery: 0.9.*
- phpunit/phpunit: ~4.0
README
从aacotroneo/laravel-saml2分支而来。目的是增强现有代码以支持多个IDP的配置。
Laravel 5 - Saml2
一个基于OneLogin工具包的Laravel包,用于将Saml2集成到作为SP(服务提供者)的Laravel中,该工具包比simplesamlphp SP更轻量且易于安装。它不需要单独的路由或会话存储即可工作!
此库的目标是尽可能简单。我们不会干扰Laravel用户、认证、会话等。我们更喜欢限制自己只完成一个具体的任务。要求用户在IDP上进行认证并处理响应。SLO请求也是如此。
安装 - Composer
您可以通过composer安装此包
composer require nirajp/laravel-saml2
或者手动将其添加到您的composer.json中
"nirajp/laravel-saml2": "*"
如果您使用的是Laravel 5.5及以上版本,服务提供者将自动注册。
对于Laravel的旧版本(<5.5),您必须将服务提供者和别名添加到config/app.php中
'providers' => [ ... Aacotroneo\Saml2\Saml2ServiceProvider::class, ] 'alias' => [ ... 'Saml2' => Aacotroneo\Saml2\Facades\Saml2Auth::class, ]
然后使用php artisan vendor:publish --provider="Aacotroneo\Saml2\Saml2ServiceProvider"
发布配置文件。这将添加文件app/config/saml2_settings.php
和app/config/saml2/test_idp_settings.php
。
test_idp_settings.php配置几乎直接由OneLogin处理,因此您可以在那里找到更多参考资料,但这里将介绍真正必要的内容。还有一些其他关于路由的配置您可能需要检查,它们相当直接。
配置
定义IDP
在saml2_settings.php中定义您想配置的所有IDP的名称。如果想要使用simplesamlphp演示,可以选择将'test'作为第一个IDP,之后添加真实的IDP。
'idpNames' => ['test', 'myidp1', 'myidp2'],
配置laravel-saml2以了解每个IDP
您需要在app/config/saml2/
文件夹下为每个IDP创建一个单独的配置文件。例如,myidp1_idp_settings.php
。您可以将test_idp_settings.php
作为起点;只需将其复制到app/config/saml2/
并重命名即可。
配置选项在此项目中未详细说明,因为它们来自OneLogin项目,请参阅那里以获取详细信息。
此配置与OneLogin使用的配置之间唯一的真正区别是,SP entityId、assertionConsumerService URL和singleLogoutService URL是由库注入的。如果您没有在相应的IDP配置的可选值中指定这些URL,则此库提供默认值,这些库为每个IDP创建的路由,其路由名称为"{$idpName}_metadata"、"{$idpName}_acs"和"{$idpName}_sls"。
如果您想使用ENV变量而不是*_idp_settings文件来定义值,您会在其中看到ENV值遵循一个命名模式。例如,如果在myidp1_idp_settings.php中设置$this_idp_env_id = 'MYIDP1';
,并在myidp2_idp_settings.php中设置它为'SECONDIDP'
,那么您可以将ENV变量设置为以SAML2_MYDP1_
和SAML2_SECONDIDP_
开头,例如。
SAML2_MYIDP1_SP_x509="..." SAML2_MYIDP1_SP_PRIVATEKEY="..." // Other SAML2_MYIDP1_* values SAML2_SECONDIDP_SP_x509="..." SAML2_SECONDIDP_SP_PRIVATEKEY="..." // Other SAML2_SECONDIDP_* values
传递给IDP配置的URL
您不需要实现SP entityId、assertionConsumerService URL和singleLogoutService路由,因为Saml2Controller已经实现了,但您需要将这些URL提供给您实际IDP的配置,即您要求其进行用户认证的第三方。
您可以通过导航到'http://laravel_url/myidp1/metadata' / 'https://laravel_url/myidp1/metadata'来检查实际的路线,这意外地将是此SP的entityId。
如果您在saml2_settings.php中配置了可选的routesPrefix
设置,那么所有IDP路由都将以前缀值开头,因此您需要相应地调整元数据URL。例如,如果将routesPrefix配置为'single_sign_on'
,则myidp1的IDP元数据将位于http://laravel_url/single_sign_on/myidp1/metadata。
示例:simplesamlphp IDP配置
例如,如果您使用simplesamlphp,并且元数据URL为http://laravel_url/myidp1/metadata
,请将以下内容添加到/metadata/sp-remote.php,以通知IDP您的laravel-saml2 SP身份
$metadata['http://laravel_url/myidp1/metadata'] = array( 'AssertionConsumerService' => 'http://laravel_url/myidp1/acs', 'SingleLogoutService' => 'http://laravel_url/myidp1/sls', //the following two affect what the $Saml2user->getUserId() will return 'NameIDFormat' => 'urn:oasis:names:tc:SAML:2.0:nameid-format:persistent', 'simplesaml.nameidattribute' => 'uid' );
用法
当您希望用户登录时,只需调用Saml2Auth::login()
或重定向到路由'saml2_login'。只需记住,它不使用任何会话存储,因此如果您要求它登录,无论用户是否已登录,它都会重定向到IDP。例如,您可以更改您的身份验证中间件。
public function handle($request, Closure $next) { if ($this->auth->guest()) { if ($request->ajax()) { return response('Unauthorized.', 401); } else { return Saml2::login(URL::full()); //return redirect()->guest('auth/login'); } } return $next($request); }
自Laravel 5.3以来,您可以在app/Exceptions/Handler.php
中更改您的未认证方法。
protected function unauthenticated($request, AuthenticationException $exception) { if ($request->expectsJson()) { return response()->json(['error' => 'Unauthenticated.'], 401); } return Saml2Auth::login(); }
Saml2::login将用户重定向到IDP,并返回到库在/myidp1/acs(或routesPrefix/myidp1/acs)提供的端点。这将处理响应,并在准备就绪时触发一个事件。您下一步要做的就是处理该事件。您只需登录用户或拒绝即可。
Event::listen('Aacotroneo\Saml2\Events\Saml2LoginEvent', function (Saml2LoginEvent $event) { $messageId = $event->getSaml2Auth()->getLastMessageId(); // Add your own code preventing reuse of a $messageId to stop replay attacks $user = $event->getSaml2User(); $userData = [ 'id' => $user->getUserId(), 'attributes' => $user->getAttributes(), 'assertion' => $user->getRawSamlAssertion() ]; $laravelUser = //find user by ID or attribute //if it does not exist create it and go on or show an error message Auth::login($laravelUser); });
身份验证持久性
请小心必要的Laravel中间件以在会话中保持身份验证持久性。
例如,它可以是这样
# in App\Http\Kernel
protected $middlewareGroups = [
'web' => [
...
],
'api' => [
...
],
'saml' => [
\App\Http\Middleware\EncryptCookies::class,
\Illuminate\Cookie\Middleware\AddQueuedCookiesToResponse::class,
\Illuminate\Session\Middleware\StartSession::class,
],
并在config/saml2_settings.php
/**
* which middleware group to use for the saml routes
* Laravel 5.2 will need a group which includes StartSession
*/
'routesMiddleware' => ['saml'],
注销
现在用户有两种注销方式。
- 1 - 通过在您的应用程序中注销:在这种情况下,您“应该”首先通知IDP,以便它关闭全局会话。
- 2 - 通过注销全局SSO会话。在这种情况下,如果IDP支持SLO,IDP将在/myidp1/slo端点(已提供)通知您
对于情况1,调用Saml2Auth::logout();
或将用户重定向到注销路由,例如'myidp1_logout',它只是这样做。不要立即关闭会话,因为您需要从IDP接收响应确认(重定向)。该响应将由库在/myidp1/sls处理,并将为您触发一个事件以完成操作。
对于情况2,您将只接收事件。两种情况1和2都接收相同的事件。
注意,对于情况2,您可能必须手动保存您的会话,以便注销生效(因为会话由中间件保存,但OneLogin库将在发生之前将您重定向回您的IDP)
Event::listen('Aacotroneo\Saml2\Events\Saml2LogoutEvent', function ($event) { Auth::logout(); Session::save(); });
就是这样。请随时提问,提出PR或建议,或打开问题。