Auth Session 退出他人正登录的账号、passport 退出登录

最近上一个项目迭代刚上线,在原有基础(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, '管理员账号或密码不正确');
     }
本作品采用《CC 协议》,转载必须注明作者和本文链接
《L01 基础入门》
我们将带你从零开发一个项目并部署到线上,本课程教授 Web 开发中专业、实用的技能,如 Git 工作流、Laravel Mix 前端工作流等。
《L02 从零构建论坛系统》
以构建论坛项目 LaraBBS 为线索,展开对 Laravel 框架的全面学习。应用程序架构思路贴近 Laravel 框架的设计哲学。
讨论数量: 1

revoke 可以直接 $request->user()->token()->revoke();

4年前 评论
aen233 (楼主) 4年前

讨论应以学习和精进为目的。请勿发布不友善或者负能量的内容,与人为善,比聪明更重要!
php @ abc
文章
20
粉丝
94
喜欢
197
收藏
231
排名:107
访问:8.9 万
私信
所有博文
社区赞助商