life2016 / yii2-sso
Requires
- php: >=7.3.0
- ext-json: *
- jasny/immutable: ^2.1
- psr/log: ^1.1
- yiisoft/yii2: *
Requires (Dev)
- codeception/codeception: ^4.1
- codeception/module-phpbrowser: ^1.0
- codeception/module-rest: ^1.2
- desarrolla2/cache: ^3.0
- jasny/http-message: ^1.3
- jasny/php-code-quality: ^2.6.0
- jasny/phpunit-extension: ^0.3.2
- phpstan/phpstan: ^0.12.59
- yubb/loggy: ^2.1
This package is auto-updated.
Last update: 2024-09-23 17:27:24 UTC
README
PHP单点登录(支持Ajax)
Jasny SSO是单点登录(SSO)的一个相对简单直接解决方案。
使用SSO,登录一个网站会验证您对所有联盟网站的访问。这些网站不需要共享顶级域名。
工作原理
在使用SSO时,可以区分3方
- 客户端 - 这是访问者的浏览器
- 代理 - 访问的网站
- 服务器 - 存储用户信息和凭证的地方
代理有一个id和一个秘密。这些双方都知晓。
当客户端访问代理时,它会创建一个随机令牌,该令牌存储在cookie中。然后代理将客户端发送到服务器,同时传递代理的id和令牌。服务器使用代理id、代理秘密和令牌创建一个哈希。这个哈希用于创建指向用户会话的链接。链接创建后,服务器将客户端重定向回代理。
代理可以使用令牌(来自cookie)、代理id和代理秘密创建相同的链接哈希。在请求时,它将哈希作为会话id传递。
服务器会注意到会话id是一个链接,并使用链接会话。因此,代理和客户端正在使用相同的会话。当另一个代理加入时,它也会使用相同的会话。
要了解更多详细信息,请阅读这篇文章。
这与OAuth有什么不同?
使用OAuth,您可以在外部服务器上验证用户并获取他们的配置文件信息。然而,您并没有共享会话。
用户使用Google OAuth登录到网站foo.com。然后他访问了使用Google OAuth的网站bar.org。无论如何,他仍然需要在bar.org上点击“登录”按钮。
Jasny SSO中,两个网站都使用相同的会话。因此,当用户访问bar.org时,他会自动登录。当他退出(在任一网站上)时,他都会退出。
安装
通过composer安装此库
composer require life2016/yii2-sso
演示
有一个演示服务器和两个演示代理作为示例。一个使用正常的重定向,另一个使用JSONP / AJAX。
为了证明它正在工作,您应该设置服务器和两个或更多代理,每个代理都在自己的机器上,有自己的(子)域名。但是,您也可以在自己的机器上运行服务器和代理,以便进行测试。
在*nix(Linux / Unix / OSX)上运行
php -S localhost:8000 -t demo/server/
export SSO_SERVER=https://:8000/attach.php SSO_BROKER_ID=Alice SSO_BROKER_SECRET=8iwzik1bwd; php -S localhost:8001 -t demo/broker/
export SSO_SERVER=https://:8000/attach.php SSO_BROKER_ID=Greg SSO_BROKER_SECRET=7pypoox2pc; php -S localhost:8002 -t demo/broker/
export SSO_SERVER=https://:8000/attach.php SSO_BROKER_ID=Julius SSO_BROKER_SECRET=ceda63kmhp; php -S localhost:8003 -t demo/ajax-broker/
现在打开一些标签并访问
注意,在登录后,您需要在其他代理上刷新才能看到效果。
用法
服务器
Server
类将一个回调作为第一个构造函数参数。此回调应根据id查找代理的秘密。
第二个参数必须是一个PSR-16兼容的缓存对象。它用于存储代理令牌和客户端会话之间的链接。
use Jasny\SSO\Server\Server; $brokers = [ 'foo' => ['secret' => '8OyRi6Ix1x', 'domains' => ['example.com']], // ... ]; $server = new Server( fn($id) => $brokers[$id] ?? null, // Unique secret and allowed domains for each broker. new Cache() // Any PSR-16 compatible cache );
在此示例中,代理被简单地配置为数组。但通常您希望从数据库中获取代理信息。
附加
客户端需要通过向服务器发出HTTP请求将代理令牌附加到会话id。此请求可以通过调用attach()
来处理。
attach()
方法返回一个验证码。此代码必须返回给代理,因为它是计算校验和所需的。
$verificationCode = $server->attach();
如果无法附加(例如,在校验和错误的情况下),则抛出异常。
处理代理API请求
在客户端会话附加到代理令牌后,代理可以代表客户端发送API请求。通过调用startBrokerSession()
方法来基于载体令牌启动客户端会话。这意味着这些请求可以通过$_SESSION
访问客户端会话信息。
$server->startBrokerSession();
代理可以使用此功能进行登录、注销、获取用户信息等。处理此类请求的API超出了项目范围。但是,由于代理使用正常会话,因此可以使用现有的认证。
如果您正在寻找认证库,请考虑使用Jasny Auth。
PSR-7
默认情况下,该库与像$_GET
和$_SERVER
这样的超全局变量一起工作。或者它可以使用PSR-7服务器请求。这可以作为参数传递给attach()
和startBrokerSession()
。
$verificationCode = $server->attach($serverRequest);
会话接口
默认情况下,该库使用超全局变量$_SESSION
和php_session_*()
函数。它是通过实现SessionInterface
的GlobalSession
对象来做的。
对于使用替代会话的项目,可以创建一个实现SessionInterface
的包装器。
use Jasny\SSO\Server\SessionInterface; class CustomerSessionHandler implements SessionInterface { // ... }
withSession()
方法创建一个具有自定义会话接口的Server对象的副本。
$server = (new Server($callback, $cache)) ->withSession(new CustomerSessionHandler());
withSession()
方法还可以与模拟对象一起用于测试。
日志记录
启用日志记录以进行调试和捕获问题。
$server = (new Server($callback, $cache)) ->withLogging(new Logger());
可以使用任何PSR-3兼容的记录器,例如Monolog或Loggy。上下文可能包含代理ID、令牌和会话ID。
代理
在创建Broker
实例时,需要传递服务器URL、代理ID和代理密钥。代理ID和密钥需要与服务器上注册的密钥匹配。
注意:代理ID必须是字母数字的。
附加
在代理可以在客户端代表进行API请求之前,客户端需要将代理令牌附加到客户端会话。为此,客户端必须向SSO服务器发出HTTP请求。
getAttachUrl()
方法将为客户端生成一个代理令牌,并使用它来创建一个附加URL。该方法接受一个查询参数数组作为单一参数。
有多种方法可以使客户端执行HTTP请求。代理可以重定向客户端或通过AJAX或加载图像使用浏览器进行请求。
use Jasny\SSO\Broker\Broker; // Configure the broker. $broker = new Broker( getenv('SSO_SERVER'), getenv('SSO_BROKER_ID'), getenv('SSO_BROKER_SECRET') ); // Attach through redirect if the client isn't attached yet. if (!$broker->isAttached()) { $returnUrl = (!empty($_SERVER['HTTPS']) ? 'https://' : 'http://') . $_SERVER['HTTP_HOST'] . $_SERVER['REQUEST_URI']; $attachUrl = $broker->getAttachUrl(['return_url' => $returnUrl]); header("Location: $attachUrl", true, 303); echo "You're redirected to <a href='$attachUrl'>$attachUrl</a>"; exit(); }
验证
在验证后,SSO服务器将返回一个验证码(作为查询参数或在JSON响应中)。该代码用于计算校验和。验证码防止使用附加链接进行会话劫持。
if (isset($_GET['sso_verify'])) { $broker->verify($_GET['sso_verify']); }
API请求
一旦附加,代理就可以代表客户端执行API请求。这可以通过以下方式完成
- 使用代理
request()
方法,或通过 - 使用任何HTTP客户端,如Guzzle
代理请求
// Post to modify the user info
$broker->request('POST', '/login', $credentials);
// Get user info
$user = $broker->request('GET', '/user');
request()
方法使用Curl发送HTTP请求,添加载体令牌进行身份验证。它期望一个JSON响应并将其自动解码。
HTTP库(Guzzle)
要使用类似Guzzle或Httplug的库,请使用getBearerToken()
获取载体令牌并设置Authorization
头
$guzzle = new GuzzleHttp\Client(['base_uri' => 'https://sso-server.example.com']); $res = $guzzle->request('GET', '/user', [ 'headers' => [ 'Authorization' => 'Bearer ' . $broker->getBearerToken() ] ]);
客户端状态
默认情况下,代理使用Cookies
类通过cookies($_COOKIE
和setcookie()
)来持久化客户端的SSO令牌。
Cookie
使用自定义参数实例化一个新的Cookies
对象,以修改cookie TTL、域名和仅https等设置。
use Jasny\SSO\Broker\{Broker,Cookies}; $broker = (new Broker(getenv('SSO_SERVER'), getenv('SSO_BROKER_ID'), getenv('SSO_BROKER_SECRET'))) ->withTokenIn(new Cookies(7200, '/myapp', 'example.com', true));
(该cookie永远不能被浏览器访问。)
Session
另外,您可以使用SessionState
将SSO令牌存储在代理的PHP会话中。
use Jasny\SSO\Broker\{Broker,Session}; session_start(); $broker = (new Broker(getenv('SSO_SERVER'), getenv('SSO_BROKER_ID'), getenv('SSO_BROKER_SECRET'))) ->withTokenIn(new Session());
自定义
该方法接受任何实现了ArrayAccess
的对象,允许您在需要时创建自定义处理程序。
class CustomStateHandler implements \ArrayAccess { // ... }
这也可以与模拟对象一起用于测试。