前言
在前后端分离的情况下,做权限系统首先要确定粒度问题,在本文中谈论的前端粒度为菜单级别(暂时不考虑按钮级别),后端的粒度就是接口。
设计思路
其实前后端分离的权限系统的主要控制还是在后端,前端只是一个简单的拦截。大体思路就是在用户登录成功之后,后端接口将用户下的所有菜单返回,前端存起来展示给用户,后端在接受到请求时,判断此请求者对于此接口是否有权限如果没有就返回403,否则继续执行。
表结构
user(id, email, name, password, token);
role(id, name,comment);
user_role(id, user_id, role_id);
menu(id, parent_id, name, route, comment);
permission(id, name, route)
role_menu(id, role_id, menu_id)
role_permission(id, role_id, permission_id)
menu_permission(id, menu_id, permission_id)
实现
后端实现
// 这是一个中间件,对于任何请求判断是否有权限
class CheckAccess {
public function handle(Request $request, Closure $next)
{
$user = get_loggined_user(); //获取请求接口的用户
$userRepo = new UserRepository($user);
if ($userRepo->isSuperAdmin()) { //判断用户是否为超级管理员
return $next($request); //超级管理员拥有所有接口的权限
}
try {
$permission = $this->getPermission($request); //这个方法根据请求路由去表permission中查询,是否将接口加入权限表如果没有添加,表示没有权限,具体细节看getPermission方法。
} catch (PermissionNotFoundException $e) {
return response(['code'=>403001,'message'=>'没有权限'], 403);
}
if ($userRepo->hasPermission($permission)) { //判断这个用户是否拥有权限,这个判断其实就是先找出用户的角色,去role_permission表中查找是否可以找到,如果找到就表示有权限。
return $next($request);
}
else {
return response(['code'=>403001,'message'=>'没有权限'], 403);
}
}
protected function getPermission($request)
{
$route = '/'.$request->path();
$uri = '/'.$request->route()->uri();
$dbroute = substr($route,strlen(env('PREFIX')));
$dburi = substr($uri,strlen(env('PREFIX')));
$permissionRepo = new PermissionRepository(new Permission());
$perg = '/\{[\w]*\?{0,1}\}/';
if ($route == $uri) {
$permission = $permissionRepo->findByRoute($dbroute);
} else {
try {
$permission = $permissionRepo->findByRoute($dbroute);
} catch (PermissionNotFoundException $e) {
$permission = $permissionRepo->findByRoute(preg_replace($perg, '*', $dburi)); //这个主要是解决 ‘/users/{id}/index’ 这一类的路由,在数据库中应该存‘/users/*/index’
}
}
return $permission;
}
}
class User{
public function menus(int $userId)
{
try{
$user = $this->userRepo->findById($userId); //获取请求的用户
} catch (UserNotFoundException $e) {
return response($this->getErrorMessage(ErrorCodeClass::OBJ_NOT_FOUND, $this->userRepo.ErrorCodeClass::OBJ_NOT_FOUND_MSG), HttpCodeClass::NOT_FOUND);
}
$this->userRepo = new UserRepository($user);
$menus = $this->userRepo->getMenus(); //查找用户下的所有菜单,供前端展示
$menuTree = $this->menuRepo->changeTree($menus->where(FieldConstants::IS_SHOW, 1)->all());
$resource = new MenuResource($menuTree);
return response($resource->transformMenu(), HttpCodeClass::OK);
}
}
前端实现
前端通过请求后端的menus将获取到菜单展示在菜单栏,到目前为止虽然权限控制的功能实现了,但是大家有没有想过这种将前端菜单和后端权限独立开的设计,如果要开发一个新功能,就需要将将菜单和接口权限都添加给角色,这里我们有使用了menu_permission这个表,将权限和菜单关联起来,其实他两的关联关系只是为了前端添加权限的时候方便
网友评论