Auth Session 退出他人正登入的賬號、passport 退出登入

aen233發表於2019-12-25

最近上一個專案迭代剛上線,在原有基礎(1個後臺、2個小程式)上,增加了3個後臺、1個小程式(兩個角色登陸同一個小程式),共5種角色,想了想,擴充套件原有的後臺管理員表,4個後臺使用session,小程式使用passport。

需求點 :

  1. 雖然共用一張表,但是4個後臺4套session 需要互不影響
  2. (後臺和小程式)登陸管理員可以修改自己的密碼,修改後退出重新登入
  3. (後臺和小程式)超級管理員可以重置普通管理員的密碼,重置後退出被重置的賬號,跳轉登入介面

    (雖然退出他人賬號這種需求很少見,laravel5.6更新也只是可以退出當前賬號其他登陸裝置,但是產品提出且堅持,還是想辦法吧

筆記有:

  1. 資料庫修改、增加guard
  2. 路由共用
  3. 中介軟體共用
  4. 多型關聯 (主要是登陸管理員的平臺被禁用後,該管理員也不能登陸或需要退出賬號)
  5. 小程式passport的退出
  6. 退出他人正登入的賬號(超管編輯普管:刪除、停用、編輯資訊、重置密碼)
  7. 修改加密方式

0、資料庫修改、增加guard

修改遷移檔案

// admin表增加欄位,type、object_id
$table->unsignedInteger('type')->default(0)->after('status')
      ->comment('型別:1=A後臺,2=B後臺,3=C後臺,4=D後臺,5=E角色');
 $table->unsignedInteger('object_id')->default(0)->after('type')
      ->comment('所屬的物件id,如A平臺id,B平臺id,C平臺id,D平臺id');
 $table->unsignedTinyInteger('is_super')->default(2)->after('type')
      ->comment('是否是超級管理員,1=是,2=否');

config/auth.php中增加配置

 'guards' => [
        'aaa' => [
            'driver'   => 'session',
            'provider' => 'admin',
        ],
        'bbb' => [
            'driver'   => 'session',
            'provider' => 'admin',
        ],
        'ccc' => [
            'driver'   => 'session',
            'provider' => 'admin',
        ],
        'ddd' => [
            'driver'   => 'session',
            'provider' => 'admin',
        ],
        'api' => [
            'driver'   => 'passport',
            'provider' => 'passport-provider'
        ],
    ],

 'providers' => [
        'admin'=> [
            'driver' => 'eloquent',
            'model'  => \App\Models\AdminModel::class,
        ],
        'passport-provider' => [
            'driver' => 'eloquent',
            'model' => \App\Models\AdminModel::class
        ]
    ],

1、路由共用

後臺路由中有個路由引數{authGuard},該引數和guard保持一致,可以靈活的在中介軟體和介面中應用該引數。
我將登陸、退出、管理員的增刪改查放到另一個檔案,後臺和小程式都可以通過require __DIR__ . '/../routes/admin_auth.php';來複用。

<?php

/**
 * @var $router \Laravel\Lumen\Routing\Router
 *
 * 後臺管理員登入
 */
$router->group([
    'prefix' => 'admin/{authGuard}',
], function () use ($router) {
    require __DIR__ . '/../routes/admin_auth.php';
});

/**
 * 小程式登陸
 */
$router->group(['prefix' => 'api'], function () use ($router) {
    $router->post('client', [
        'uses'     => 'AuthClientController@store',
        'describe' => '管理員模組.生成客戶端',
    ]);
    $router->post('token', [
        'uses'     => 'AuthAccessTokenController@issueToken',
        'describe' => '管理員模組.生成token',
    ]);

    require __DIR__ . '/../routes/admin_auth.php';
});

小程式這裡把passport原有的生成客戶端和token的方法重寫了,
1是需要生成密碼授權的客戶端,文件中只看到了命令,沒有看到介面引數;
2是因為生成客戶端這步需要先進行業務校驗;
3是因為和前端對接返回token時還需返回當前登陸管理員的資訊。

2、中介軟體共用

當在config/auth.php增加多個guard後,就可以這樣寫 'middleware' => 'auth:aaa'
路由中:

// A後臺 a路由
$app->router->group([
    'namespace'  => 'App\Http\Controllers\AAA',
    'prefix'     => 'aaa',
    'middleware' => ['after', 'auth:aaa'] 
], function ($router) {
    require __DIR__ . '/../routes/aaa.php';
});

// B後臺 bbb路由
$app->router->group([
    'namespace'  => 'App\Http\Controllers\BBB',
    'prefix'     => 'bbb',
    'middleware' => ['after', 'auth:bbb']
], function ($router) {
    require __DIR__ . '/../routes/bbb.php';
});

// C後臺 ccc路由
$app->router->group([
    'namespace'  => 'App\Http\Controllers\CCC',
    'prefix'     => 'ccc',
    'middleware' => ['after', 'auth:ccc']
], function ($router) {
    require __DIR__ . '/../routes/ccc.php';
});

// 小程式  路由
$app->router->group([
    'namespace'  => 'App\Http\Controllers\Mini',
    'prefix'     => 'mini',
    'middleware' => ['after', 'auth:api']
], function ($router) {
    require __DIR__ . '/../routes/mini.php';
});

重寫app/Http/Middleware/Authenticate.php的handle方法,就可以在同一個中介軟體中校驗多個角色的登陸狀態、啟禁用狀態等等。

3、多型關聯

需求是登陸管理員的平臺被禁用後,該管理員也不能登陸或需要退出賬號,
思路是在中介軟體中判斷該管理員對應的平臺是否被禁用。(根據type和object_id共同判斷去哪一張表查資料)。
src/app/Models/AdminModel.php中增加

use Illuminate\Database\Eloquent\Relations\Relation;
···
public static function boot()
{
  parent::boot();
  Relation::morphMap([
  '1' => self::class,
  '2' => AModel::class,
  '3' => BModel::class,
  '4' => CModel::class,
  '5' => DModel::class,
 ]);}

public function authObject()
{
  return $this->morphTo('authObject', 'type', 'object_id');
}

其他對應平臺Model中增加

    public function authObject()
    {
        return $this->morphMany(AdminModel::class, 'authObject');
    }

應用,在src/app/Http/Middleware/Authenticate.php

   public function handle($request, Closure $next, $guard = 'api')
    {
        $guard = $request->authGuard ?? $guard;
        if (!$this->auth->guard($guard)->check()) {
            throw new BaseException(401, '賬號登入過期,請重新登入');
        }

        $authUser = $this->auth->guard($guard)->user()->load('authObject');

        // 多型關聯應用在這裡
        if ( false == $admin->authObject->status ) {
            throw new BaseException(401, '登陸平臺已被禁用');
        }

        return $next($request);
    }

4、小程式passport的退出

文件裡面沒搜出來,我發現最簡單的辦法是將oauth_access_tokens表中的revoked欄位從0改成1。

    // 如果guard是api(即小程式)編輯oauth_access_tokens表
    // 如果是後臺,直接調logout方法,logout方法也是laravel Auth開箱即用的方法
    public function logout()
    {
        'api' == $this->authGuard
            ? OauthAccessTokens::query()
            ->where('user_id', Auth::guard('api')->user()->id)
            ->update(['revoked' => 1])
            : Auth::guard($this->authGuard)->logout();

        return [];
    }

5、退出他人正登入的賬號

這裡分兩種情況,一種是禁用,一種是編輯。

搜了很多,也就找到了laravel5.6的退出其他裝置。

針對禁用

發現最簡單的辦法是,在中介軟體中判斷

 if (CorpAdminModel::STATUS_CLOSE == $admin->status) {
            throw new BaseException(401, '管理員賬號已禁用');
 }
針對編輯

想了很多辦法,比如在login時在redis中增加admin_id和session_id的關聯,編輯介面中刪除session_id,可能是我開啟方式不對,總之失敗了。後來還是用簡單粗暴的方式,加一個最後編輯時間欄位,在中介軟體中判斷

 $table->unsignedInteger('last_edit_time')->default(0)->after('pinyin')
       ->comment('最後編輯時間');

中介軟體中增加:

        // 每次登入都會更新最後登入時間,當最後編輯時間>最後登入時間,則表示是登入狀態被編輯,需退出到登入頁面
        if ($admin->last_edit_time > $admin->last_login_time) {
            if ('api' == $guard) {
                OauthAccessTokens::query()
                    ->where('user_id', $admin->id)
                    ->update(['revoked' => 1]);
            }
            throw new BaseException(401, '賬號已被編輯,請重新登陸');
        }

這裡不太好的地方是,總覺得不是最好的辦法,給校驗登入的中介軟體增加太多東西了,兩個功能的程式碼放在一起了,這裡還是需要再研究下laravel的session Auth。


我登入了3個guard的賬號,然後在中介軟體中 dd出$request->session() 結果如下,
感覺是同一個session_id,不同平臺的登入憑證是作為屬性attributes出現在session物件中的,我對這裡還沒有弄清楚....

laravel Auth 退出他人正登入的賬號(超管編輯普管:刪除、停用、編輯資訊、重置密碼)

6、修改加密方式

這條主要是吐槽,前期迭代是組內小夥伴寫的使用者認證,他的密碼加密方式是 隨機salt+md5,然後我用passport的時候,密碼校驗那裡走不過了,翻了會原始碼太深了,索性就把加密方式換成雜湊加密了。
其實salt+md5的方式,安全性應該能保證,但是你既然用laravel,用laravel推薦的方式不好咩?

        // 之前的管理員賬號新增
        $salt = substr(md5(uniqid()), 0, 6);
        $adminDetail = $this->adminService->add([
            'username' => $params['username'],
            'account' => $params['account'],
            'password' => md5($params['password'].$salt),
            'salt' => $salt,
        ]);

        // 之前的管理員密碼錯誤
        $mdPassword = md5($password.$admin->salt);
        if ($mdPassword != $admin->password) {
            throw new BaseException(200005);
        }

        // 之前的認證資訊儲存
        Auth::guard('admin')->login($admin);

修改加密方式之後

     // 管理員賬號新增
     ...
     $params['password'] = Hash::make(AdminModel::PASSWORD);
     return AdminModel::query()->create($params);

     // 登陸  attempt是laravel Auth開箱即用的方法,作用是校驗及登陸
     if (!Auth::guard($authGuard)->attempt($params)) {
         throw new BaseException(567001, '管理員賬號或密碼不正確');
     }

相關文章