ibpavlov/lumen-saml2

一个Lumen包,用于作为服务提供者(SP)进行Saml2集成,针对多个身份提供者(IdP),基于OneLogin工具包,比simplesamlphp更轻量级(从aacotroneo/laravel-saml2分支而来)。

3.0.3 2019-08-21 14:05 UTC

This package is auto-updated.

Last update: 2024-09-22 02:13:45 UTC


README

Build Status

这是一个基于OneLogin工具包的Lumen包,用于Saml2集成作为服务提供者(SP),比simplesamlphp SP更轻便、更容易安装。它不需要单独的路线或会话存储即可工作!

这个库的目标是尽可能简单。我们不会干扰Lumen用户、认证、会话……我们更倾向于限制自己只完成一个具体任务。要求用户在IDP处进行认证并处理响应。SLO请求也是如此。

从"aacotroneo/laravel-saml2"分支并更新以与Lumen兼容

安装 - Composer

您可以通过Composer安装此包

composer require ibpavlov/lumen-saml2

或者手动将其添加到您的composer.json中

"ibpavlov/lumen-saml2": "*"

然后从vendor/ibpavlov/lumen-saml2/config复制配置文件到config文件夹。然后根据您的配置自定义新文件。

应用配置文件并在bootstrap/app.php中注册服务

$app->configure('saml2_settings');
$app->configure('saml2_test');

$app->register(Ibpavlov\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文件中,您会在那里看到ENV值的命名模式。例如,如果在myidp1_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')函数实例化一个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,扩展Saml2Controller类并实现自己的login()函数。将saml2_settings的值saml2_controller设置为您的扩展类,以便路由将请求定向到您的控制器而不是默认。
例如:
saml_settings.php

    'saml2_controller' => 'App\Http\Controllers\MyNamespace\MySaml2Controller'

MySaml2Controller.php

use Ibpavlov\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('Ibpavlov\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会话中的身份验证持久性所需的必要中间件。

例如,可以是

# 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库将重定向到您的身份提供者)

        Event::listen('Ibpavlov\Saml2\Events\Saml2LogoutEvent', function ($event) {
            Auth::logout();
            Session::save();
        });

这就完了。欢迎提问、提出PR或建议,或者打开问题。