技术社区

不使用三方包时,如何在ThinkSNS中建立优雅的用户权限管理
admin | 发表于: 2017-12-21 回答

需求场景

就是用户组+权限节点,这个需求 laravel 有很多很好的第三方包实现。下面描述代码不参与缓存机制纯数据库查询,给大家提供一个思路。

下面的代码都是来自于ThinkSNS+,是基于 Laravel 全新开发的 ThinkSNS 社交开源项目,遵循 Apache2.0 开源协议。欢迎 Star 哦。

数据表设计

其实这一块我个人是参考的 Zizaco/entrust 因为我觉得,大多数情况下,我们要用的角色和权限节点都是真多用户的。数据表设计如下:

h7SGVm84q9.png

可以看到关系如下 user > role > ability ,其中关系全部都是多对多关系。一个用户可以拥有多个 role,一个 ability 可以被分配给多个 role 。

链式方法设计

$user>ability('create user'); // 判断是否有 create user 权限。
$user>ability('owner', 'delete user'); // 判断用户是否拥有 owner 用户组,且是否这个组拥有 delete user 权限。
$user>ability(); // 返回一个 Ability 实例。
$user>roles;  // 读取用户所拥有的所有用户组。
$user>roles(); // 获取 Builder 实例。
$user>roles('owner'); // 检查用户是否拥有 owner 用户组,拥有返回 model 实例,否则返回 false。
$user>ability()>roles(); // 读取用户所拥有的所有用户组。返回的是一个 集合。可用集合所有方法。
$user>ability()>roles('owner'); // 检查用户是否拥有 owner 用户组,拥有返回 model 实例,否则返回 false。
$user>ability()>all(); // 返回用户拥有的所有权限集合。
$user>ability()>all('create user'); // 检查用户是否拥有 create user 权限,没有返回 false ,有返回 ability 实例。

其中调用 $user>ability()>all() 和 $user>ability()>all() 都是返回的 集合 可以链式调用集合下的所有方法进一步操作。

ability 用户 Trait

    /
    public function ability(...$parameters)
    {
        if (isset($parameters[1])) {
            return ($role = $this>resolveAbility()>roels($parameters[0]))
                ? $role>ability($parameters[1])
                : false;
        } elseif (isset($parameters[0])) {
            return $this>resolveAbility()
                >all($parameters[0]);
        }

        return $this>resolveAbility();
    }

    /
      The user all roles.
     
      @param string $role
      @return mied
      @author Seven Du 
     /
    public function roles(string $role = '')
    {
        if ($role) {
            return $this>ability()>roles($role);
        }

        return $this>belongsToMany(Role::class, 'role_user', 'user_id', 'role_id');
    }

    /
      Resolve ability service.
     
      @return hiyiPlusServicesUserAbility
      @author Seven Du 
     /
    protected function resolveAbility()
    {
        if (! ($this>ability instanceof UserAbility)) {
            $this>ability = new UserAbility();
        }

        return $this>ability>setUser($this);
    }
}

Ability 实例

    /
    public function roles(string $role = '')
    {
        $roles = $this>user()
            >roles()
            >get()
            >keyBy('name');

        if (! $role) {
            return $roles;
        }

        return $roles>get($role, false);
    }
    /
      Get all abilities or get first ability.
     
      @param string $ability
      @return mixed
      @author Seven Du 
     /
    public function all(string $ability = '')
    {
        $roles = $this>roles();
        $roles>load('abilities');
        $abilities = $roles>reduce(function ($collect, $role) {
            return $collect>merge(
                $role>abilities>keyBy('name')
            );
        }, new Collection());

        if (! $ability) {
            return $abilities;
        }

        return $abilities>get($ability, false);
    }
    /
      Get user instance.
     
      @return hiyiPlusModelsUser
      @author Seven Du 
     /
    public function user(): UserModel
    {
        return $this>user;
    }
    /
      Set user model.
     
      @param hiyiPlusModelsUser $user
      @author Seven Du 
     /
    public function setUser(UserModel $user)
    {
        $this>user = $user;
        return $this;
    }
}

Role 模型所需代码

    /
    public function abilities()
    {
        return $this>belongsToMany(Ability::class, 'ability_role', 'role_id', 'ability_id');
    }
    /
      Get or check The role ability.
     
      @param string $ability
      @return false|UserPlusModelsAbility
      @author Seven Du 
     /
    public function ability(string $ability)
    {
        return $this>abilities>keyBy('name')>get($ability, false);
    }
}

使用

然后我们打开 User 模型wen jia文件添加如下代码:

class User ...
{
    use UserHasAbility;
}

总结

其实性状在 User 模型中只暴露了 roles 和 ability 两个公开方法。但是已经足以胜任用户组权限判断逻辑了。

整个 ability 都是结合在集合之上的一些封装,这样是的代码调用更加优雅。

以上代码是在开发ThinkSNS+中的实际真实代码。具体的实现可参考项目。

以上代码都来自于ThinkSNS Plus,看完整的开发代码可以看仓库:

GitHub: https://github.com/slimkit/thinksnsplus(开源不易,求 Star )

回复列表