Auth Session 退出他人正登录的账号、passport 退出登录
最近上一个项目迭代刚上线,在原有基础(1个后台、2个小程序)上,增加了3个后台、1个小程序(两个角色登陆同一个小程序),共5种角色,想了想,扩展原有的后台管理员表,4个后台使用session,小程序使用passport。
需求点 :
- 虽然共用一张表,但是4个后台4套session 需要互不影响
- (后台和小程序)登陆管理员可以修改自己的密码,修改后退出重新登录
- (后台和小程序)超级管理员可以重置普通管理员的密码,重置后退出被重置的账号,跳转登录界面
(虽然退出他人账号这种需求很少见,laravel5.6更新也只是可以退出当前账号其他登陆设备,但是产品提出且坚持,还是想办法吧
笔记有:
- 数据库修改、增加guard
- 路由共用
- 中间件共用
- 多态关联 (主要是登陆管理员的平台被禁用后,该管理员也不能登陆或需要退出账号)
- 小程序passport的退出
- 退出他人正登录的账号(超管编辑普管:删除、停用、编辑信息、重置密码)
- 修改加密方式
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对象中的,我对这里还没有弄清楚....
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 协议》,转载必须注明作者和本文链接
推荐文章: