閱讀461 返回首頁    go 技術社區[雲棲]


不使用三方包時,如何在ThinkSNS中建立優雅的用戶權限管理

本文主要全麵講解在不適用第三方包的情況下,如何在基於Laravel框架上,研發社交係統ThinkSNS+時,簡曆一套優雅而不失性價比的用戶權限管理體係功能,【內含ThinkSNS真實代碼】。

需求場景

就是用戶組+權限節點,這個需求 laravel 有很多很好的第三方包實現。下麵描述代碼不參與緩存機製純數據庫查詢,給大家提供一個思路。

下麵的代碼都是來自於ThinkSNS+,是基於 Laravel 全新開發的 ThinkSNS 社交開源項目,遵循 Apache-2.0 開源協議。歡迎 Star 哦。

數據表設計

其實這一塊我個人是參考的 Zizaco/entrust 因為我覺得,大多數情況下,我們要用的角色和權限節點都是真多用戶的。數據表設計如下:

h7SGVm84q9

可以看到關係如下 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

<?php

namespace Zhiyi\Plus\Models\Concerns;

use Zhiyi\Plus\Models\Role;
use Zhiyi\Plus\Services\UserAbility;

trait UserHasAbility
{
/**
* Abiliry service instance.
*
* @var \Zhiyi\Plus\Services\UserAbility
*/
protected $ability;

/**
 * User ability.
 *
 * @param array $parameters
 *        ability();
 *        ability($ability);
 *        ability($role, $ability);
 * @return mixed
 * @author Seven Du <shiweidu@outlook.com>
 */
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 <shiweidu@outlook.com>
 */
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 \Zhiyi\Plus\Services\UserAbility
 * @author Seven Du <shiweidu@outlook.com>
 */
protected function resolveAbility()
{
    if (! ($this->ability instanceof UserAbility)) {
        $this->ability = new UserAbility();
    }

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

}
Ability 實例

<?php
namespace Zhiyi\Plus\Services;
use Illuminate\Support\Collection;
use Zhiyi\Plus\Models\User as UserModel;
use Zhiyi\Plus\Contracts\Model\UserAbility as UserAbilityContract;

class UserAbility implements UserAbilityContract
{
protected $user;

/**
 * Get all roles or get first role.
 *
 * @param string $role
 * @return mixed
 * @author Seven Du <shiweidu@outlook.com>
 */
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 <shiweidu@outlook.com>
 */
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 \Zhiyi\Plus\Models\User
 * @author Seven Du <shiweidu@outlook.com>
 */
public function user(): UserModel
{
    return $this->user;
}
/**
 * Set user model.
 *
 * @param \Zhiyi\Plus\Models\User $user
 * @author Seven Du <shiweidu@outlook.com>
 */
public function setUser(UserModel $user)
{
    $this->user = $user;
    return $this;
}

}
Role 模型所需代碼

<?php
namespace Zhiyi\Plus\Models;
use Illuminate\Database\Eloquent\Model;

class Role extends Model
{
/**
* Get all abilities of the role.
*
* @return \Illuminate\Database\Eloquent\Relations\BelongsToMany
* @author Seven Du shiweidu@outlook.com
/
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|\User\Plus\Models\Ability
* @author Seven Du shiweidu@outlook.com
*/
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/thinksns-plus
(開源不易,求 Star )

ThinkSNS產品免費體驗:https://www.thinksns.com/experience.html

最後更新:2017-11-03 15:04:01

  上一篇:go  【TensorFlow開源2年官方回顧】下一個重要方向是分布式模型服務
  下一篇:go  任正非:將打造華為統一的AI平台,2018首先在GTS部署