分析
当访问某个路由时,判断当前访问的用户对应哪个角色,再判断该角色是否能够有进入此路由的权限。因此:
- 需要建立
users
表,roles
表,permissions
表和permission_role
表。permissions
表存储路由,permission_role
表存储角色拥有的路由权限 - 需要鉴权的路由需要经过一个中间件去管理当前用户是否拥有访问的权限
- 用户鉴权通过后需要存储当前访问的角色信息,以便后续代码根据不同的角色执行不同的动作
建立迁移文件
// create_users_table
$table->increments('id');
$table->unsignedInteger('role_id')->comment('用户的角色 id');
$table->string('name', 24)->comment('用户姓名');
$table->string('phone', 11)->unique()->comment('用户手机号');
$table->string('password', 32)->comment('用户密码');
$table->timestamps();
// create_roles_table
$table->increments('id');
$table->string('name', 24)->comment('角色的英文名称');
$table->string('alias', 24)->comment('角色的中文名称');
$table->timestamps();
// create_permissions_table
$table->increments('id');
$table->string('route', 256)->comment('路由 url');
$table->string('desc', 32)->comment('路由的描述');
$table->timestamps();
// create_permission_role_table
$table->increments('id');
$table->unsignedInteger('role_id')->comment('角色 id');
$table->unsignedInteger('permission_id')->comment('权限 id');
$table->timestamps();
建立模型关联
app/Models/User.php
public function role()
{
return $this->belongsTo('App\\Models\\Role');
}
app/Models/Role.php
public function users()
{
return $this->hasMany('App\\Models\\User');
}
public function permissions()
{
return $this->belongsToMany('App\\Models\\Permission')
->withTimestamps();
}
编写鉴权中间件
app/Http/Middleware/CheckPermission.php
<?php
namespace App\\Http\\Middleware;
use App\\Models\\Role;
use App\\Models\\User;
use Closure;
class CheckPermission
{
public function handle($request, Closure $next)
{
if (!$request->has('user_id')) {
return response()->json(['status' => 403, 'msg' => '请携带 user_id 参数'], 403);
}
// 获取当前用户信息
$user = User::find($request->user_id);
// 获取当前用户可以进入的路由
$availableRoutes = Role::with('permissions:route')->find($user->role_id)->permissions->toArray();
// 如果当前路由在权限内,可以进入
return in_array($request->path(), array_column($availableRoutes, 'route'))
? $next($request)
: response()->json(['status' => 403, 'msg' => '您没有访问权限'], 403);
}
}
注册鉴权中间件
此处以 Lumen 举例。在 bootstrap/app.php
中添加代码
$app->routeMiddleware([
'check_permission' => App\\Http\\Middleware\\CheckPermission::class,
]);
记录授权后的用户信息和角色信息
如果使用 laravel 原生的授权方法,可以根据需求修改 app/Providers/AuthServiceProvider.php
。
还可以在 app/Http/Controllers/Controller.php
中的 __construct
方法中手动记录。
protected $currentUser;
protected $currentRole;
public function __construct(Request $request)
{
if ($request->has('user_id')) {
$this->currentUser = User::with('role')->find($request->user_id);
$this->currentRole = $this->currentUser->role->name;
}
}
可优化点
- 目前通过读取
Request $request
中的 user_id 实现用户鉴权,这是由于本项目的架构所限,但实际上可以直接使用 Laravel 的 Token 机制 - 可以使用 Laravel 自带的授权和策略功能,比如获取用户信息只需要使用
$request->user()
即可,不需要在Controller.php
的__construct
中手动查询用户信息 roles
和permissions
表一般情况下读操作远大于写操作,因此非常适合将表数据进行缓存,从而尽可能减少从数据库中的查询permissions
中目前仅存储了路由名称作为权限的判断条件,但假如使用标准的 restful 风格 api,那么get /resources
和POST /resources
在鉴权时将视为同一个路由。因此应当在表中存入更多的 request 信息以做区分