本文實例講述了Laravel5權限管理的實現方法。分享給大家供大家參考,具體如下:
關于權限管理的思考
最近用laravel設計后臺,后臺需要有個權限管理。權限管理實質上分為兩個部分,首先是認證,然后是權限。認證部分非常好做,就是管理員登錄,記錄session。這個laravel中也有自帶Auth來實現這個。最麻煩就是權限認證。
權限認證本質上就是誰有權限管理什么東西。這里有兩個方面的維度,誰,就是用戶維度,在用戶維度,權限管理的粒度可以是用戶一個人,也可以是將用戶分組,如果將用戶分組,則涉及到的邏輯是一個用戶可以在多個組里面嗎?在另外一方面,管理什么東西,這個東西是物的維度,一個頁面是一個東西,一個頁面上的一個元素也是一個東西,或者往大了說,一個功能是一個東西。所以做權限管理最重要的是確認這兩個維度的粒度。這個已經不是技術的事情了,這個是需要需求討論的了。
基于上面的思考,我這次想做的權限管理,在用戶維度,是基于個人的。就是每個人的權限不一樣。在東西的維度,我設置路由為最小的單位,即可以為單個路由設置權限管理。
下面的思考就是使用什么來標記權限,可以使用位,也可以使用字符,也可以使用整型。后來我選擇了字符,基于兩點考慮:1 字符淺顯易懂,在數據庫中查找也比較方便 2 我沒有按照某個權限查找有這個權限的人的需求,即沒有反查需求,使用位,整型等都意義不大。
接下來考慮如何和laravel結合,既然要為每個路由設置訪問權限,那么我當然希望能在laravel的route.php路由管理中配置。最好就是在Route::get的時候有個參數能設置permission。這樣做的好處是權限設置簡易了。在決定路由的時候,就順手寫了權限控制。壞處呢,也很明顯,laravel路由的三種方式只能寫一種了。就是Route::(method)這樣的方式了。
基本決定好了就開干。
路由設計
基本的路由是這樣的
這里在基本的制定路由action之后設置了一個permissions的屬性,這個屬性設計成數組,因為比如一個post請求,它可能在某個頁面會觸發,也可能在另外一個頁面觸發,那么這個post請求就需要同時擁有兩個頁面路由的權限。
這里使用admin.validate的權限控制,這樣,可以將權限分組,admin都是關于admin相關的分組,在數據庫中,我就會存儲一個二維數組,[admin] => ['validate', 'index']; 存儲成二維數組而不是一維的好處呢,一般后臺展示是有兩個維度的,一個是頭部的tab欄,一個是左邊的nav欄,就是說這個二維的數組和后臺的tab,nav欄是一一對應的。
中間件設計
好了,下面我們就掛上中間件,并且設置所有的路由都走這個中間件
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
|
<?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有個:
1
|
admin−>hasPermission(admin−>hasPermission(permit); |
這個就涉及到model的設計。
model設計
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
|
<?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中判斷
1
2
|
@ if ( $admin ->hasPermission( 'admin.index' )) @ endif |
就可以判斷這個用戶是否可以看到這個tab了。
總結
這個是一個不算復雜的用戶權限實現,但是我感覺已經能滿足大部分的后臺需求了。當然可以優化的點可能很多,
比如permission是不是可以支持正則,hasPermission如果存儲在nosql或者pg中,是不是不用進行json的數據解析,直接一個DB請求就能判斷是否有permission之類的?
希望本文所述對大家基于Laravel框架的PHP程序設計有所幫助。