授權
簡介
laravel 除了提供開箱即用的授權服務,還提供了許多簡單的方式來管理授權邏輯和資源的存取控制。這些各式的方法和協助函數便於你管理你的授權邏輯。我們將在本章中對其進行一一的解讀。
定義能力
判斷一個使用者是否具有執行給定動作的能力的最簡單的方式就是使用 Illuminate\Auth\Access\Gate 類去定義相應的能力。laravel 所提供的 AuthServiceProvider 類是定義這些能力的推薦位置。讓我們來看個樣本,我們定義一個 update-post 的能力,這個能力接收一個當前 User 和一個 Post 模型。在這個能力中,我們需要判斷使用者的 id 與 post 的 user_id 是否匹配:
registerPolicies($gate); $gate->define('update-post', function ($user, $post) { return $user->id === $post->user_id; }); }}
注意上面的樣本中我們並沒有檢查所給定的 $user 是否為 NULL。這是因為當給定的使用者沒有經過認證或者使用者沒有經過 forUser 方法指定,Gate 類會自動的為所有的能力返回 false。
基於類的能力
除了使用 Closures 的方式作為授權檢查的回調來註冊能力,你還可以通過傳遞類中的方法來進行能力的註冊,當需要的時候,類會通過服務容器來解析:
$gate->define('update-post', 'Class@method');
授權檢查攔截器
有時候,你可能需要向某些特殊使用者發放所有能力的通行證,這個時候,你可以使用 before 方法來定義一個回調,它會在所有的授權檢查之前運行:
$gate->before(function ($user, $ability) { if ($user->isSuperAdmin()) { return true; }});
如果 before 的回呼函數返回一個非空的結果,那麼該結果將作為檢查的結果。
你也可以使用 after 方法來定義一個在每個能力授權檢查之後執行的回呼函數,但是,你不能在這個回呼函數內修改檢查的結果:
$gate->after(function ($user, $ability, $result, $arguments) { //});
檢查能力
通過 Gate 假面
一旦一個能力被定義完成,我們就可以通過多種方式來進行能力的檢查。首先,我們可以使用 Gate 假面的 check,allows,或者 denies 方法。所有的這些方法都會接收能力的名稱,並且會把額外的參數傳遞給相應能力的回呼函數中。你並不需要傳遞當前的使用者到這些方法中,Gate 會自動的前置目前使用者到參數中並傳遞給能力的回呼函數。所以當我們檢查早前定義的 update-post 能力時,我們只需要傳遞 Post 的執行個體到 denies 方法中就可以了:
當然,allows 方法與 denies 相反,如果動作被授權通過則返回 true. check 方法就是 allows 方法的別名。
對指定的使用者檢查能力
如果你想要使用 Gate 假面來檢查非當前經授權通過使用者的其他使用者是否具備相應的能力,你可以使用 forUser 方法:
if (Gate::forUser($user)->allows('update-post', $post)) { //}
傳遞多個參數
當然,能力的回呼函數可以接收多個參數:
Gate:define('delete-comment', function ($user, $post, $comment) { // });
如果你的能力需要接收多個參數,你可以簡單的通過 Gate 假面的方法進行傳遞一個經多個參數所組成的數組:
if (Gate::allows('delete-comment', [$post, $comment])) { //}
通過使用者模型檢查能力
事實上,你可以通過 User 模型的執行個體來檢查使用者的能力。預設的 laravel 的 App\User 模型使用了 Authorizable trait,這個性狀包含兩個方法:can 和 cannot。這兩個方法和 Gate 假面的 allows 和 denies 方法的用法相同。我們還使用上面曾使用過的例子,修改成如下:
user()->cannot('update-post', $post)) { abort(403); } // Update Post... }}
當然,can 方法與 cannot 的相反:
if ($request->user()->can('update-post', $post)) { // Update Post...}
在 Blade 模板中檢查能力
為了方便,laravel 提供了 @can Blade 指令來快速的檢查當前授權的使用者是否具有指定的能力。比如:
id }}">View Post@can('update-post', $post) id }}/edit">Edit Post@endcan
你也可以通過 @else 指令來配合 @can 指令:
@can('update-post', $post) @else @endcan
在表單請求中檢查能力
你也可以通過使用表單請求(繼承自 Request,用於表單驗證的請求類)中自訂的 authoriza 方法來驗證 Gate 假面中定義的能力:
/** * Determine if the user is authorized to make this request. * * @return bool */ public function authorize() { $postId = $this->route('post'); return Cate::allows('update', Post::findOrFail($postId)); }
策略(Policies)
建立策略
為了不讓你把所有的授權邏輯全部放進 AuthServiceProvider 從而使應用增長為一個龐大而笨重的應用。 laravel 允許你通過 Policy 類來分離你的授權邏輯。策略類其實就是一個包含授權邏輯組的原生 PHP 類。
首先,讓我們來產生一個策略來管理我們的 Post 的授權。你可以通過 make:policy 命令來產生一個策略。所產生的策略會存放在 app/Policies 目錄:
php artisan make:policy PostPolicy
註冊策略
一旦策略存在,我們還需要在 Gate 類中進行註冊。在 AuthServiceProvider 中包含了一個 policies 屬性,該屬性存放所有實體與策略間的映射。所以,我們需要將 Post 模型的策略指定到 PostPolice 類:
PostPolicy::class, ]; /** * Register any application authentication / authorization services. * * @param \Illuminate\Contracts\Auth\Access\Gate $gate * @return void */ public function boot(GateContract $gate) { $this->registerPolicies($gate); }}
編寫策略
一旦策略被產生和註冊後,我們就可以為所有能力的授權添加驗證方法。例如,讓我們在 PostPolicy 類中定義一個 update 方法,用來驗證所給定的使用者是否具有 update Post 的能力:
id === $post->user_id; }}
你可以繼續在策略中添加其他所需進行授權驗證的方法。比如,你可以繼續為驗證 Post 的各種動作而定義 show,destroy,或者 addComment 方法。
注意:所有的策略類都是通過服務容器解析而來。這意味著你可以使用類型提示來在策略類的建構函式中進行依賴注入所需要的依賴。
攔截所有檢查
有時候,你可能需要發放給指定使用者具有所有能力的通行證,這時候,你可以在策略類中定義 before 方法。該方法會在策略中其他所有方法被執行前運行:
public function before($user, $ability){ if ($user->isSuperAdmin()) { return true; }}
如果 before 方法返回一個非空值,那麼其結果將會用來作為授權驗證的結果的判斷依據。
檢查策略
策略類的方法和基於授權回調的方法一樣通過相同的方式作為 Closure 被調用。你可以使用 Gate 假面,User 模型,@can Blade 指令或者 policy helper 來進行授權的檢查。
通過 Gate 假面
Gate 會通過檢查傳遞給方法中參數的類型來確定應該使用哪一種策略。所以,如果我們傳遞 Post 執行個體到 denies 方法,Gate 將會自動的使用相對應的 PostPolicy 來進行授權驗證:
通過使用者的模型
User 模型中的 can 和 cannot 方法也會在所給定參數可用時自動匹配相應的策略。這些方法提供了一種便利的方式去驗證任意使用者執行個體是否具有所給定的能力:
if ($user->can('update', $post)) { //}if ($user->cannot('update', $post)) { //}
通過 Blade 模板
就像我們所期望的,@can Blade 指令會在所給定參數可用時自動匹配相應的策略:
@can('update', $post) @endcan
通過策略 Helper
全域協助方法 policy 可以通過所給定的類解析相應的 Policy 類。例如,我們可以傳遞一個 Post 執行個體到 policy 協助方法,該方法會返回相應的 PostPolicy 類:
if (policy($post)->update($user, $post)) { //}
控制器授權
預設的,在 laravel 中基於 Ap\Http\Controllers\Controller 的類都引入了 AuthorizesRequests trait(性狀)。該性狀提供了 authorize 方法來快速的驗證所給定的動作是否有執行的能力,如果不具備相應的能力會拋出一個 HttpException 。
authorize 方法共用了其它授權方法的簽證方式,如 Gate::allows 和 $user->can()。那麼,讓我們來使用 authorize 方法快速的鑒別一個請求是否具有更新 Post 的能力:
authorize('update', $post); // Update Post... }}
如果這個動作通過了授權,控制器將繼續執行下面的邏輯。否則會自動的拋出一個 HttpException 錯誤,這個錯誤會產生一個 403 Not Authorized 的 Http 響應。就如你所看到的, authorize 方法是一個非常方便的方法,它的方便之處就在於只使用一條語句執行了授權的驗證或拋出異常。
AuthorizeRequests trait 也提供了 authorizeForUser 方法來進行非目前使用者的使用者與給定能力的鑒權:
$this->authorizeForUser($user, 'update', $post);
自動的確定策略方法
通常的,策略類中的方法是與控制器中的方法相對應的。比如在上面的 update 方法中,控制器的方法和策略的方法使用了相同的命名: update。
由於這個原因,laravel 允許你通過簡單的傳遞一個執行個體參數到 authorize 方法,在能力的評鑑中,laravel 會根據當前方法的命名自動的確定策略方法的調用。在上面的例子中,由於 authorize 方法是在控制器中的 update 方法中調用的,所以 PostPolicy 中的 update 將會被調用:
/** * Update the given post. * * @param int $id * @return Response */ public function update($id) { $post = Post::findOrFail($id); $this->authorize($post); // Update Post... }