Laravel5作權限管理

關於權限管理的思考

最近用laravel設計後臺,後臺須要有個權限管理。權限管理實質上分爲兩個部分,首先是認證,而後是權限。認證部分很是好作,就是管理員登陸,記錄session。這個laravel中也有自帶Auth來實現這個。最麻煩就是權限認證。php

權限認證本質上就是誰有權限管理什麼東西。這裏有兩個方面的維度,誰,就是用戶維度,在用戶維度,權限管理的粒度能夠是用戶一我的,也能夠是將用戶分組,若是將用戶分組,則涉及到的邏輯是一個用戶能夠在多個組裏面嗎?在另一方面,管理什麼東西,這個東西是物的維度,一個頁面是一個東西,一個頁面上的一個元素也是一個東西,或者往大了說,一個功能是一個東西。因此作權限管理最重要的是確認這兩個維度的粒度。這個已經不是技術的事情了,這個是須要需求討論的了。laravel

基於上面的思考,我此次想作的權限管理,在用戶維度,是基於我的的。就是每一個人的權限不同。在東西的維度,我設置路由爲最小的單位,便可覺得單個路由設置權限管理。sql

下面的思考就是使用什麼來標記權限,可使用位,也可使用字符,也可使用整型。後來我選擇了字符,基於兩點考慮:1 字符淺顯易懂,在數據庫中查找也比較方便 2 我沒有按照某個權限查找有這個權限的人的需求,即沒有反查需求,使用位,整型等都意義不大。數據庫

接下來考慮如何和laravel結合,既然要爲每一個路由設置訪問權限,那麼我固然但願能在laravel的route.php路由管理中配置。最好就是在Route::get的時候有個參數能設置permission。這樣作的好處是權限設置簡易了。在決定路由的時候,就順手寫了權限控制。壞處呢,也很明顯,laravel路由的三種方式只能寫一種了。就是Route::(method)這樣的方式了。json

基本決定好了就開幹。數組

路由設計

基本的路由是這樣的session

Route::post('/admin/validate', ['uses' => 'AdminController@postValidate', 'permissions'=>['admin.validate', 'admin.index']]);

這裏在基本的制定路由action以後設置了一個permissions的屬性,這個屬性設計成數組,由於好比一個post請求,它可能在某個頁面會觸發,也可能在另一個頁面觸發,那麼這個post請求就須要同時擁有兩個頁面路由的權限。nosql

這裏使用admin.validate的權限控制,這樣,能夠將權限分組,admin都是關於admin相關的分組,在數據庫中,我就會存儲一個二維數組,[admin] => ['validate', 'index']; 存儲成二維數組而不是一維的好處呢,通常後臺展現是有兩個維度的,一個是頭部的tab欄,一個是左邊的nav欄,就是說這個二維的數組和後臺的tab,nav欄是一一對應的。函數

中間件設計

好了,下面咱們就掛上中間件,而且設置全部的路由都走這個中間件post

<?php namespace App\Http\Middleware;

use Illuminate\Support\Facades\Session;
use Closure;

class Permission {

    /**
     * Handle an incoming request.
     *
     * @param  \Illuminate\Http\Request  $request
     * @param  \Closure  $next
     * @return mixed
     */
    public function handle($request, Closure $next)
    {
        $permits = $this->getPermission($request);

        $admin = \App\Http\Middleware\Authenticate::getAuthUser();

        // 只要有一個有權限,就能夠進入這個請求
        foreach ($permits as $permit) {
            if ($permit == '*') {
                return $next($request);
            }
            if ($admin->hasPermission($permit)) {
                return $next($request);
            }
        }

        echo "沒有權限,請聯繫管理員";exit;
    }

    // 獲取當前路由須要的權限
    public  function getPermission($request)
    {
        $actions = $request->route()->getAction();
        if (empty($actions['permissions'])) {
            echo "路由沒有設置權限";exit;
        }
        return $actions['permissions'];
    }
}

這裏最關鍵的就getPermission函數,從$request->route()->getAction()來獲取出這個路由的action定義,而後從其中的permissions字段中獲取route.php中定義的路由權限。

而後上面的middleware有個\(admin->hasPermission(\)permit); 這個就涉及到model的設計。

model設計

<?php namespace App\Models\Admin;

use App\Models\Model as BaseModel;

class Admin extends BaseModel {

    protected $table = 'admin';

    // 判斷是否有某個權限
    public function hasPermission($permission)
    {
        $permission_db = $this->permissions;
        if(in_array($permission, $permission_db)) {
            return true;
        }

        return false;
    }

    // permission 是一個二維數組
    public function getPermissionsAttribute($value)
    {
        if (empty($value)) {
            return [];
        }
        $data = json_decode($value, true);
        $ret = [];
        foreach ($data as $key => $value) {
            $ret[] = $key;
            foreach ($value as $value2) {
                $ret[] = "{$key}.{$value2}";
            }
        }
        return array_unique($ret);
    }

    // 全局設置permission
    public function setPermissionsAttribute($value)
    {
        $ret = [];
        foreach ($value as $item) {
            $keys = explode('.', $item);
            if (count($keys) != 2) {
                continue;
            }
            $ret[$keys[0]][] = $keys[1];
        }

        $this->attributes['permissions'] = json_encode($ret);
    }
}

在數據庫中,我將二維數組存儲爲json,利用laravel的Attribute的get和set方法,完成了數據庫中json和外界程序邏輯的鏈接。而後hasPermission就顯得很輕鬆了,直接判斷in_array就ok了。

後續

這個權限認證的邏輯就清晰了。而後若是頁面中某個tab或者nav須要對不一樣權限的用戶展現,只須要在view中判斷

@if ($admin->hasPermission('admin.index')) 
@endif

就能夠判斷這個用戶是否能夠看到這個tab了。

總結

這個是一個不算複雜的用戶權限實現,可是我感受已經能知足大部分的後臺需求了。固然能夠優化的點可能不少, 好比permission是否是能夠支持正則,hasPermission若是存儲在nosql或者pg中,是否是不用進行json的數據解析,直接一個DB請求就能判斷是否有permission之類的?

相關文章
相關標籤/搜索