aacotroneo / laravel-saml2
一个用于多身份提供者(IdP)的Laravel包,作为SAML2集成服务提供商(SP),基于OneLogin工具包,比simplesamlphp更轻量。
Requires
- php: >=5.5.0
- ext-openssl: *
- illuminate/support: >=5.0.0
- onelogin/php-saml: ^3.0.0
Requires (Dev)
- mockery/mockery: 0.9.*
- phpunit/phpunit: ~4.0
README
该项目不再维护。我很乐意转让所有权,或者您也可以很容易地用许多分支之一替换它(如果有人想在这里列出他们的分支或一些其他库,请告诉我)。库本身变化不大,但有时需要更新以保持与Laravel和PHP版本更新的同步。
Laravel 5 - Saml2
一个基于OneLogin工具包的Laravel包,用于SAML2集成作为SP(服务提供商),比simplesamlphp SP更轻便且易于安装。它不需要单独的路由或会话存储即可工作!
该库的目标是尽可能简单。我们不会干扰Laravel用户、认证、会话等。我们更喜欢限制自己只做具体任务。请用户在IDP上进行认证并处理响应。SLO(单一注销)请求也是如此。
安装 - Composer
您可以通过composer安装此包
composer require aacotroneo/laravel-saml2
或将以下内容手动添加到您的composer.json中
composer.json
"aacotroneo/laravel-saml2": "*"
如果您使用的是Laravel 5.5及以上版本,服务提供程序将自动注册。
对于Laravel的旧版本(<5.5),您必须添加服务提供程序
config/app.php
'providers' => [ ... Aacotroneo\Saml2\Saml2ServiceProvider::class, ]
然后使用php artisan vendor:publish --provider="Aacotroneo\Saml2\Saml2ServiceProvider"
发布配置文件。这将添加app/config/saml2_settings.php
和app/config/saml2/mytestidp1_idp_settings.php
文件,您需要对其进行自定义。
mytestidp1_idp_settings.php
配置几乎直接由OneLogin处理,因此请参考那里的完整细节,但我们将在这里介绍真正必要的内容。还有一些其他关于路由的配置,您可以检查一下,它们相当直接。
配置
定义IDP
在saml2_settings.php
中定义您想要配置的所有IDP的名称。如果您想使用simplesamlphp演示,可以选择保留mytestidp1
(区分大小写)作为第一个IDP,并在其后添加真实的IDP。IDP的名称将显示在此库创建的SAML2路由的URL中,以及每个IDP配置的内部文件名。
config/saml2_settings.php
'idpNames' => ['mytestidp1', 'test', 'myidp2'],
配置laravel-saml2以了解每个IDP
您需要在app/config/saml2/
文件夹下为每个IDP创建一个单独的配置文件。例如,test_idp_settings.php
。您可以使用mytestidp1_idp_settings.php
作为起点;只需复制并重命名即可。
此项目未解释配置选项,因为它们来自OneLogin项目,请参考那里以获取详细信息。
此配置与OneLogin使用的配置之间唯一的真正区别是,SP的entityId
、assertionConsumerService
URL和singleLogoutService
URL是由库注入的。
如果您未在相应的IDP配置的可选值中指定URL,则该库提供默认值。该库为每个IDP创建metadata
、acs
和sls
路由。如果您在配置中指定不同的值,请注意,acs
和sls
URL应与您设置的指向相应Saml2Controller函数的实际路由相对应。
如果您想选择性地在环境变量中定义值,而不是在 {idpName}_idp_settings
文件中,您会看到其中有一个命名模式,您可以遵循环境变量的命名。例如,如果在 mytestipd1_idp_settings.php
中设置 $this_idp_env_id = 'mytestidp1';
,然后在 myidp2_idp_settings.php
中设置 $this_idp_env_id = 'myidp2'
,那么您可以分别设置以 SAML2_mytestidp1_
和 SAML2_myidp2_
开头的环境变量。
例如,可以是以下内容:
.env
SAML2_mytestidp1_SP_x509="..." SAML2_mytestidp1_SP_PRIVATEKEY="..." // Other SAML2_mytestidp1_* values SAML2_myidp2_SP_x509="..." SAML2_myidp2_SP_PRIVATEKEY="..." // Other SAML2_myidp2_* values
传递给IDP配置的URL
如上所述,您不需要实现SP的entityId
、assertionConsumerService
URL 和 singleLogoutService
URL 路由,因为 Saml2Controller 默认已经实现了。但是您需要了解这些路由,以便将这些路由提供给您的实际IDP配置,即您请求验证用户的第三方。
您可以通过访问 http(s)://{laravel_url}/{idpName}/metadata
来检查实际的路线,例如 http(s)://{laravel_url}/mytestidp1/metadata
,这将是此SP的默认entityId。
如果您在 saml2_settings.php
中配置可选的 routesPrefix
设置,则所有IDP路由都将以前面值作为前缀,因此您需要相应地调整元数据URL。例如,如果您将 routesPrefix
配置为 'single_sign_on'
,则您的 mytestidp1
IDP元数据将可在 http(s)://{laravel_url}/single_sign_on/mytestidp1/metadata
中找到。
库为每个IDP自动创建的路由是
{routesPrefix}/{idpName}/logout
{routesPrefix}/{idpName}/login
{routesPrefix}/{idpName}/metadata
{routesPrefix}/{idpName}/acs
{routesPrefix}/{idpName}/sls
示例:simplesamlphp IDP配置
如果您使用simplesamlphp作为测试IDP,并且您的SP元数据URL为 http(s)://{laravel_url}/mytestidp1/metadata
,请将以下内容添加到 /metadata/sp-remote.php
,以通知IDP您的laravel-saml2 SP身份。
例如,可以是以下内容:
/metadata/sp-remote.php
$metadata['http(s)://{laravel_url}/mytestidp1/metadata'] = array( 'AssertionConsumerService' => 'http(s)://{laravel_url}/mytestidp1/acs', 'SingleLogoutService' => 'http(s)://{laravel_url}/mytestidp1/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' );
用法
当您想要用户登录时,只需将用户重定向到特定IDP的登录路由,route('saml2_login', 'mytestidp1')
。您还可以使用 Saml2Auth::loadOneLoginAuthFromIpdConfig('mytestidp1')
函数实例化所需的IDP的 Saml2Auth
,以加载配置并构造OneLogin认证参数;只需记住它不使用任何会话存储,所以如果您要求它登录,无论用户是否已经登录,它都会重定向到IDP。例如,您可以将身份验证中间件更改为以下内容。
例如,可以是以下内容:
App/Http/Middleware/RedirectIfAuthenticated.php
public function handle($request, Closure $next) { if ($this->auth->guest()) { if ($request->ajax()) { return response('Unauthorized.', 401); // Or, return a response that causes client side js to redirect to '/routesPrefix/myIdp1/login' } else { $saml2Auth = new Saml2Auth(Saml2Auth::loadOneLoginAuthFromIpdConfig('mytestidp1')); return $saml2Auth->login(URL::full()); } } 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); // Or, return a response that causes client side js to redirect to '/routesPrefix/myIdp1/login' } $saml2Auth = new Saml2Auth(Saml2Auth::loadOneLoginAuthFromIpdConfig('mytestidp1')); return $saml2Auth->login('/my/redirect/path'); }
对于通过重定向到登录路由(如 {routesPrefix}/mytestidp1/login
)进入的登录请求,默认的登录调用不会将重定向URL传递给Saml登录请求。该登录参数很有用,因为ACS处理程序可以获取该值(从IDP作为RelayPath返回),默认情况下将重定向到那里。要从控制器登录传递重定向URL,扩展Saml2Controller类并实现自己的 login()
函数。将 config/saml2_settings.php
中的 saml2_controller
值设置为您的扩展类,以便路由将请求重定向到您的控制器而不是默认控制器。
例如,可以是以下内容:
config/saml_settings.php
'saml2_controller' => 'App\Http\Controllers\MyNamespace\MySaml2Controller'
App/Http/Controllers/MyNamespace/MySaml2Controller.php
use Aacotroneo\Saml2\Http\Controllers\Saml2Controller; class MySaml2Controller extends Saml2Controller { public function login() { $loginRedirect = '...'; // Determine redirect URL $this->saml2Auth->login($loginRedirect); } }
登录调用后,用户将被重定向到IDP登录页面。然后配置了您使用库提供的端点的IDP将调用回,例如 /mytestidp1/acs
或 /{routesPrefix}/mytestidp1/acs
。这将处理响应并在准备就绪时触发一个事件。下一步是您需要处理该事件。您只需登录用户或拒绝。
例如,可以是以下内容:
App/Providers/MyEventServiceProvider.php
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中间件以在Session中实现身份验证持久性。
例如,可以是以下内容:
App/Http/Kernel.php
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,它将在
/mytestidp1/slo
端点通知您(已提供)。
对于情况1,调用Saml2Auth::logout();
或将用户重定向到登出路由,例如mytestidp1_logout
,该路由仅执行此操作。不要立即关闭会话,因为您需要从IDP接收响应确认(重定向)。该响应将由库在/mytestidp1/sls
处处理,并将触发一个事件以供您完成操作。
对于情况2,您将只接收到事件。情况1和2都会接收到相同的事件。
请注意,对于情况2,您可能需要手动保存您的会话以使登出生效(因为会话由中间件保存,但OneLogin库将在发生之前将您重定向回您的IDP)。
例如,可以是以下内容:
App/Providers/MyEventServiceProvider.php
Event::listen('Aacotroneo\Saml2\Events\Saml2LogoutEvent', function ($event) { Auth::logout(); Session::save(); });
这就是全部内容。请随时提问、提交PR或建议,或打开问题。