通过Cookie构建基于Session的单点使用
原创单点登录和实施思路。
单点登录(Single Sign On),缩写 SSO这意味着在多个应用程序系统中,用户只能访问彼此信任的所有其他应用程序一次。通常用于同一公司不同子系统之间的登录认证。
实现单点登录的方式有很多种,这里我们只介绍两种,一种是基于。 Cookie 凭证,这种方法适用于子系统之间一致的主域名,因为只有这样才能共享不同的子系统。 Cookie;另一个是通过 CAS 实现 SSO 系统中,该方案适用于所有场景,只是比较 Cookie 理解和实现凭据更加复杂。
让我们从一个简单的直通开始。 Cookie 简单单点登录的凭据。
具体的思路和我们前面说的话 单页应用程序 API 认证 有些是相似的,但现在我们不同的子系统是分开的,域名也不同。例如,主系统是 blog.test
,该子系统是 sub.blog.test
。Laravel 添加 Cookie 响应的默认域名为当前系统域名,所以需要写 Cookie 域名是额外设置的,以确保主系统和子系统可以共享。 Cookie 凭证。
另外,我们让子系统和主系统共享用户表,子系统不需要单独设置用户表,通过子系统获取用户信息。 API 从主系统获取,这需要我们自己的实现 UserProvider 以获取用户信息。
创建新的测试应用程序
首先,我们创建一个新的测试项目 subapp
:
composer create-project laravel/laravel subapp --prefer-dist -vvv
并设置其域名 sub.blog.test
。
登录中心 Cookie 设置
单点登录需要单独的登录中心,我们使用主系统。 blog
作为登录中心,所有子系统的登录请求都跳到这里。
设置 Cookie 域名
我们会掌握这个系统 Cookie 角色域名已设置 .blog.test
登录成功后,通过 CreateFreshApiToken
中间件可以将令牌写入 Cookie,Session ID 它也将被写成 Cookie, 这允许在子系统中读取这些内容。 Cookie并将此信息带到请求标头中。
Cookie 域名设置 config/session.php
中的配置项 domain
为了实现这一目标,我们加入了进来。 .env
环境配置中的设置 SESSION_DOMAIN
可以完成设置:
SESSION_DOMAIN=.blog.test
接下来,我们将进入 app/Http/Kernel.php
中添加 CreateFreshApiToken
中间件(需要提前安装)。 Passport ):
protected $middlewareGroups = [
web => [
...
\Laravel\Passport\Http\Middleware\CreateFreshApiToken::class,
],
...
注意:如果您之前已经进行了配置,则可以跳过此步骤。
然后,在 routes/api.php
向用户添加一条新的路由。 ID 返回用户信息:
Route::middleware(auth:api)->group(function () {
...
Route::get(/user/{id}, function ($id) {
return \App\User::find($id);
});
});
设置 Session 存储媒介
对于基于 Session 对于单点登录的实现,我们也让主系统和子系统共享用户。 Session 信息,使用户可以在多个系统中阅读。 Session从而确定登录状态。我们可以通过 Session 存储到 Redis 为了达到这一要求,需要将其安装在登录中心(主系统)。 predis
扩展:
composer require predis/predis
然后在 .env
环境配置中的修改 SESSION_DRIVER
:
SESSION_DRIVER=redis
以及 Redis 相关配置值,以便您可以连接到系统。 Redis。
此时,登录中心( blog
应用程序)已经配置好了,让我们回到子系统。 subapp
。
在子系统定制中。 UserProvider 实现
回到子系统项目,我们需要定制一个。 UserProvider 实现以从主系统获取经过身份验证的用户信息,在 app
在目录下创建一个。 Extensions
子目录,并在该子在目录下创建一个。 SsoUserProvider
类:
<?php
namespace App\Extensions;
use GuzzleHttp\Client;
use GuzzleHttp\Cookie\CookieJar;
use Illuminate\Auth\EloquentUserProvider;
class SsoUserProvider extends EloquentUserProvider
{
public function retrieveById($identifier)
{
$http = new Client();
$cookies = CookieJar::fromArray([
laravel_token => $_COOKIE[laravel_token]
], .blog.test);
$response = $http->request(GET, http://blog.test/api/user/ . $identifier, [
cookies => $cookies
]);
$user = json_decode($response->getBody()->getContents(), TRUE);
$model = $this->createModel();
$model->forceFill($user);
return $model;
}
}
在这个定制类中,我们通过了。 API 界面获取用户信息并返回。
然后将 config/auth.php
的 providers
配置项修改如下:
providers => [
users => [
driver => sso,
model => App\User::class,
],
// users => [
// driver => database,
// table => users,
// ],
],
最后,在 AuthServiceProvider
的 boot
该自定义在该方法中注册。 UserProvider 类使其有效:
// 在文件定义中引入以下命名空间。
use App\Extensions\SsoUserProvider;
use Illuminate\Support\Facades\Auth;
public function boot()
{
...
// 通过自定义 EloquentUserProvider 覆盖系统默认设置
Auth::provider(sso, function ($app, $config) {
return new SsoUserProvider($app->make(hash), $config[model]);
});
}
子系统 Cookie 及 Session 相关配置
以获取登录中心用户在子系统中的权限。 Session,该子系统需要 Session 相关配置设置与登录中心相同,我们已进入。 .env
在配置文件中配置:
SESSION_DRIVER=redis
SESSION_LIFETIME=120
SESSION_DOMAIN=.blog.test
同样,还需要一个子系统 subapp
中安装 predis
扩展:
composer require predis/predis
然后在 .env
中对 Redis 进行配置,以便您可以连接。这里的配置需要与登录中心一致,才能流畅地阅读用户。 Session。
此外,该子系统的 APP_KEY
配置方式与登录中心相同,保证安全。 Cookie 及 Session 加密和解密结果一致。
在子系统中测试单点登录
在测试单点登录功能之前,在子系统中。 subapp
中先运行 make:auth
命令生成系统附带身份验证脚手架代码:
php artisan make:auth
然后我们修改 LoginController
将登录请求重定向到系统主登录中心:
// 将登录页面重定向到登录中心。
public function showLoginForm()
{
return redirect(http://blog.test/login);
}
将退出请求发送到后台登录中心退出:
// 在文件顶部引入适当的命名空间。
use GuzzleHttp\Client;
use GuzzleHttp\Cookie\CookieJar;
// 向登录中心发送退出请求并清除本地会话。
public function logout(Request $request)
{
$http = new Client();
$cookies = CookieJar::fromArray([
laravel_session => $_COOKIE[laravel_session],
XSRF-TOKEN => $_COOKIE[XSRF-TOKEN]
], .blog.test);
$response = $http->request(POST, http://blog.test/logout, [
cookies => $cookies
]);
if ($response->getStatusCode() == 200) {
$request->session()->invalidate();
return $this->loggedOut($request) ?: redirect(/);
}
abort(500);
}
最后在 RegisterController
将注册请求重定向到后台登录中心:
public function showRegistrationForm()
{
return redirect(http://blog.test/register);
}
注意:这里我们只测试登录、注册和退出过程。如需跳转,请办理其他认证相关流程。
让我们测试登录过程并在子系统中访问它。 http://sub.blog.test/login
,页面将跳转到系统主登录中心:
填写登录表并提交登录,返回子系统,访问 http://sub.blog.test/home
,您会发现您已成功登录:
子系统退出后,进入主系统,发现已全部成功退出。
注册过程类似。如果登录中心设置了邮箱认证,则在子系统判断用户是否通过认证时,也要开启邮箱认证中间件。
另外,这里还有一个细节,就是从子系统跳转到登录中心并成功登录后,需要跳转到对应的子系统页面,可以通过 Session 将退回地址设置为实现,即登录中心根据源系统登录成功后。 URL 往后跳。
基于 Cookie 基于实施 Session 单点登录有其局限性,即主域名需要在不同的系统之间保持一致,否则无法生效,所以我们将介绍一个更通用的基于 CAS 单点登录解决方案。
版权声明
所有资源都来源于爬虫采集,如有侵权请联系我们,我们将立即删除