Laravel 通過 cookie 實現基於 session 的單點登入

自由與溫暖是遙不可及的夢想發表於2020-07-14

單點登入說明

單點登入(Single Sign On),簡稱為 SSO,意思是在多個應用系統中,使用者只需要登入一次就可以訪問所有相互信任的其它應用系統。一般常用於同一家公司的不同子系統之間的登入認證。

需要外掛

//  passport 
composer require laravel/passport
// predis
composer require predis/predis

首先我們建立三個專案

composer create-project --prefer-dist laravel/laravel login.sso.test
composer create-project --prefer-dist laravel/laravel home.sso.test
composer create-project --prefer-dist laravel/laravel my.sso.test

env 配置

APP_KEY=***

DB_CONNECTION=mysql
DB_HOST=192.168.10.10
DB_PORT=3306
DB_DATABASE=sso
DB_USERNAME=homestead
DB_PASSWORD=secret

BROADCAST_DRIVER=log
CACHE_DRIVER=redis
QUEUE_CONNECTION=redis
SESSION_DRIVER=redis
SESSION_LIFETIME=120
SESSION_DOMAIN=.sso.test

SESSION_DOMAIN 我們在這個 env 新增了這個配置 是為了設定config/session.php 中配置項 domail 設定 Cookie 域名

三個專案的 APP_KEY 需要設定為一樣否者不行

登入認證中心

安裝擴充套件包

composer require laravel/passport
composer require predis/predis

執行資料庫遷移

php artisan migrate

執行 artisan 命令

php artisan passport:install

安裝laravel 自帶 認證中心

composer require laravel/ui
npm install && npm run dev
// 會提示建立頁面 不需要的頁面可以不建立
composer require laravel/ui

config/auth.php 配置

guards = [
        ...
         'web' => [
            'driver' => 'session',
            'provider' => 'users',
        ],
        'api' => [
            'driver' => 'passport',
            'provider' => 'users',
        ],
]

配置中介軟體

protected  $middlewareGroups = [
    'wbe' => [
        ...
        \Laravel\Passport\Http\Middleware\CreateFreshApiToken::class,
   ],
  'api' => [
        ...
        'throttle:60,1',
        'bindings',
  ]
],

配置路由routes/api.php

// 為了方便測試 取消 token驗證
Laravel\Passport\Passport::$ignoreCsrfToken = true;

Route::middleware('auth:api')->group(function () {
    Route::get('/user/{id}', function ($id) {
        return \App\User::find($id);
    });
});

登入認證 中心專案完成

home.sso.test 專案

安裝擴充套件包

composer require laravel/passport
composer require predis/predis

安裝laravel自帶認證中心

composer require laravel/ui
npm install && npm run dev
// 會提示建立頁面 不需要的頁面可以不建立
// 子系統不需要登入註冊找回密碼等頁面 所以 全部選擇 no
composer require laravel/ui

自定義 Provider

我們需要自定一個 SsoUserProvider 來實現從主系統獲取使用者資訊,在 app目錄下建立一個 Extensions目錄 建立
SsoUserProvider

// 建立資料夾
mkdir Extensions
// 進入目錄
cd Extensions
// 建立檔案
touch SsoUserProvider.php

SsoUserProvider.php 程式碼

<?php

namespace App\Exceptions;

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'],
            ],
            '.sso.test'
        );

        $response = $http->request(
            'GET',
            'http://login.sso.test/api/user/'.$identifier,
            [
                'cookies' => $cookies,
            ]
        );

        $user = json_decode($response->getBody()->getContents(), true);
        $model = $this->createModel();

        $model->forceFill($user);

        return $model;
    }
}

以上域名 可以走 自定義配置檔案
這個自定義類中,我們同過API 介面獲取使用者資訊並返回

然後設定 config/auth.phpproviders配置項修改如下

 'users' => [
        'driver' => 'sso',
        'model' => App\User::class,
],

最後我們在 AuthServiceProvider .phpboot 方法註冊自定義的 UserProvider 類讓它生效

use Illuminate\Support\Facades\Auth;
use App\Exceptions\SsoUserProvider;
public function boot()
{
    ...
    Auth::provider(
        'sso',
        function ($app, $config) {
  return new SsoUserProvider($app->make('hash'), $config['model']);
        }
  );
}

然後們修改 app/Http/Controllers/Auth/LoginController.php 將登入重定向到登入系統

 public function showLoginForm()
 {
     return redirect('http://login.sso.test/login');
 }

註冊 找回密碼 同理

退出登入

我們修改 app/Http/Controllers/Auth/LoginController.php 檔案

public function logout(Request $request)
    {
        $http = new Client();
        $cookies = CookieJar::fromArray(
            [
            // 注意是 laravel_session 不是laravel_token
                'laravel_session' => $_COOKIE['laravel_session'],
                'XSRF-TOKEN' => $_COOKIE['XSRF-TOKEN'],
            ],
            '.sso.test'
        );

        $response = $http->request('POST', 'http://login.sso.test/logout', ['cookies' => $cookies]);

        if ($response->getStatusCode() == 200) {
            $request->session()->invalidate();

            return $this->loggedOut($request) ?: redirect('/');
        }
        abort(500);
    }

登入認證中心 主專案

因為 post 提交需要 _token 這裡為了方便 直接在 app/Http/Middleware/VerifyCsrfToken.php 中的 $except 新增退出路由 否者會 419 錯誤

 protected $except = [
        '/logout'
    ];

my.sso.test 同上 即可

歡迎評論 給出意見 及修改

本作品採用《CC 協議》,轉載必須註明作者和本文連結

相關文章