osi-open-source / lumen-saml2
这是一个Lumen包,用于Saml2集成,作为多个IdP(身份提供者)的SP(服务提供者),基于OneLogin工具包,比simplesamlphp更轻量级(由aacotroneo/laravel-saml2分支而来)。
Requires
- php: >=5.6.0
- ext-openssl: *
- laravel/lumen-framework: >=5.0.0
- onelogin/php-saml: ^3.0.0
Requires (Dev)
- mockery/mockery: 0.9.*
- phpunit/phpunit: ~4.0
This package is auto-updated.
Last update: 2024-09-26 04:00:58 UTC
README
这是一个基于OneLogin工具包的Lumen包,用于Saml2集成,作为SP(服务提供者)。它比simplesamlphp SP更轻量且易于安装!它不需要单独的路由或会话存储来工作!
该库的目标是尽可能简单。我们不会干扰Lumen用户、认证、会话等。我们更喜欢限制自己只处理具体任务。要求用户在IDP上进行身份验证并处理响应。SLO请求也是同样的情况。
从"aacotroneo/laravel-saml2"分支并更新为支持Lumen
安装 - Composer
您可以通过composer安装此包
composer require osi-open-source/lumen-saml2
或者手动将其添加到您的composer.json中
"osi-open-source/lumen-saml2": "*"
然后从vendor/osi-open-source/lumen-saml2/config
将配置文件复制到config
文件夹。然后根据您的配置自定义新文件。
应用配置文件并在bootstrap/app.php中注册服务
$app->configure('saml2_settings'); $app->configure('saml2_test'); $app->register(OsiOpenSource\Saml2\Saml2ServiceProvider::class);
test_idp_settings.php配置几乎直接由OneLogin处理,因此您应该参考那里的完整详细信息,但我们将在这里介绍真正必要的部分。还有一些关于路由的配置可能需要您检查,它们相当直接。
配置
定义IDP
在saml2_settings.php中定义您想要配置的所有IDP的名称。如果您想使用simplesamlphp演示,可以选择性地将'test'作为第一个IDP,之后添加真实IDP。IDP的名称将显示在由该库创建的Saml2路由的URL中,以及每个IDP的配置文件内部。
'idpNames' => ['test', 'myidp1', 'myidp2'],
配置lumen-saml2以了解每个IDP
您需要在app/config/saml2/
文件夹下为每个IDP创建一个单独的配置文件。例如,myidp1_idp_settings.php
。您可以使用test_idp_settings.php
作为起点;只需复制它并重命名。
配置选项没有在此项目中解释,因为它们来自OneLogin项目,请参考那里的详细信息。
此配置与OneLogin使用的配置之间的唯一真正区别是,SP entityId、assertionConsumerService url和singleLogoutService URL由库注入。如果您没有在相应的IDP配置的必选值中指定这些URL,此库将提供默认值:为每个IDP创建的元数据、acs和sls路由。如果指定不同的值,请注意acs和sls URL应与您设置的对应Saml2Controller函数的实际路由相对应。
如果您想选择性地在ENV变量中定义值而不是在*_idp_settings文件中,您会在其中看到可以遵循的命名模式。例如,如果在我的ipd1_idp_settings.php中设置$this_idp_env_id = 'MYIDP1';
,在myidp2_idp_settings.php中设置它为'SECONDIDP'
,那么您可以在以SAML2_MYDP1_
和SAML2_SECONDIDP_
开头的ENV变量中设置,例如。
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默认会这样做。但是您需要了解这些路由,以便将其提供给您实际IDP的配置,即您要求进行用户认证的第三方。
您可以通过导航到 'http(s)://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作为测试IDP,并且您的SP元数据URL是http://laravel_url/myidp1/metadata
,请在/metadata/sp-remote.php中添加以下内容,以通知IDP您的lumen-saml2 SP标识
$metadata['http://lumen_url/myidp1/metadata'] = array( 'AssertionConsumerService' => 'http://Lumen_url/myidp1/acs', 'SingleLogoutService' => 'http://Lumen_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' );
用法
当您想让用户登录时,只需将用户重定向到特定IDP配置的登录路由route('saml2_login', 'myIdp1')
。您也可以使用Saml2Auth::loadOneLoginAuthFromIpdConfig('myIdp1')
函数来实例化所需的IDP的Saml2Auth
,通过加载配置和构建OneLogin认证参数;只需记住,它不使用任何会话存储,因此如果您要求它登录,它将重定向到IDP,无论用户是否已经登录。例如,您可以更改您的认证中间件。
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('myIdp1')); return $saml2Auth->login(URL::full()); } } return $next($request); }
从Lumen 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('myIdp1')); return $saml2Auth->login('/my/redirect/path'); }
对于重定向到登录路由('routesPrefix/myidp1/login')的登录请求,默认登录调用不会将重定向URL传递给Saml登录请求。该登录参数非常有用,因为ACS处理器可以获取该值(从IDP作为RelayPath传递回),默认情况下将重定向到该URL。要从控制器登录传递重定向URL,扩展Saml2Controller类并实现自己的login()
函数。将saml2_settings值saml2_controller
设置为您的扩展类,以便路由将请求定向到您的控制器而不是默认。
例如:
saml_settings.php
'saml2_controller' => 'App\Http\Controllers\MyNamespace\MySaml2Controller'
MySaml2Controller.php
use OsiOpenSource\Saml2\Http\Controllers\Saml2Controller; class MySaml2Controller extends Saml2Controller { public function login() { $loginRedirect = '...'; // Determine redirect URL $this->saml2Auth->login($loginRedirect); } }
登录调用后,用户将被重定向到IDP登录页面。然后配置了端点的IDP将回调,例如/myidp1/acs
或/routesPrefix/myidp1/acs
。这将处理响应并在准备好时触发事件。您下一步要做的就是处理该事件。您只需登录用户或拒绝即可。
Event::listen('OsiOpenSource\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() ]; $LumenUser = //find user by ID or attribute //if it does not exist create it and go on or show an error message Auth::login($LumenUser); });
身份验证持久性
注意必要Lumen中间件在Session中的身份验证持久性。
例如,它可以是
# 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
* Lumen 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('OsiOpenSource\Saml2\Events\Saml2LogoutEvent', function ($event) { Auth::logout(); Session::save(); });
就这样。欢迎提出任何问题、提交PR或建议,或打开问题。