iwouldrathercode/php-custom-saml

一个基于OneLogin工具包的Laravel包,用于Saml2集成,作为多个IDP(身份提供者)的SP(服务提供者),比simplesamlphp更轻量级。

1.4.0 2020-06-23 09:06 UTC

README

Latest Version on Packagist Total Downloads

基于OneLogin的SAML PHP Toolkit的具有偏见的分支,兼容PHP 5.X & 7.X。基于OneLogin工具包,比simplesamlphp SP更轻量且易于安装。它不需要独立的路由或会话存储即可工作!

安装 - Composer

您可以通过composer安装此包

composer require iwouldrathercode/php-custom-saml

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

"iwouldrathercode/php-custom-saml": "*"

如果您正在使用Laravel 5.5及以上版本,服务提供者将自动注册。

对于Laravel的旧版本(<5.5),您必须将服务提供者添加到config/app.php

'providers' => [
        ...
    	Iwouldrathercode\Saml2\Saml2ServiceProvider::class,
]

然后使用以下命令发布配置文件:php artisan vendor:publish --provider="Iwouldrathercode\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。IDP的名称将显示在此库创建的Saml2路由的URL中,以及内部文件名中。

    'idpNames' => ['test', 'myidp1', 'myidp2'],

配置laravel-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函数的实际路由相对应。

如果您想要在*_idp_settings文件中定义值而不是在ENV变量中定义,您会在其中看到可以遵循的ENV值命名模式。例如,如果在myipd1_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 您的 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' 
);

使用方法

当您想要用户登录时,只需将用户重定向到特定 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);
}

自 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('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 Iwouldrathercode\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('Iwouldrathercode\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,通过将用户重定向到 saml2_logout 路由 (route('saml2_logout', 'myidp1')) 来启动注销。不要立即关闭会话,因为您需要从 IDP 收到响应确认(重定向)。该响应将由库在 sls 路由中处理,并将触发一个 Saml2LogoutEvent 事件,您可以使用该事件以与以下情况 2 相同的方式完成注销。

对于情况 2,您将只接收到事件。情况和 2 都接收相同的 Saml2LogoutEvent 事件。

注意,对于情况 2,您可能需要手动保存您的会话以使注销生效(因为会话由中间件保存,但 OneLogin 库将在发生之前将您重定向回您的 IDP)

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

就是这样。请随时提出任何问题,创建 PR 或建议,或打开问题。