這篇文章給大家分享的內容是關於Laravel 5.1框架中的ACL使用者授權及權限檢查功能的實現,有一定的參考價值,希望可以幫助到有需要的朋友。
1、引言
Laravel提供的開箱即用的認證功能使得使用者註冊、登入、登出和密碼重設變得便捷和簡單。
但是如果你需要控制存取網站特定部分,或讓非管理員開啟/關閉特定頁面,又或確保某些使用者只能編輯自己發佈的東西(如文章),那麼你就需要引入類似BeatSwitch Lock這樣的工具或是自己手動寫這樣的功能。我們將這樣的功能稱之為ACL:Access Control Lists(存取控制清單),用於定義使用者基於其使用者記錄屬性操作或查看特定事物的權限。
幸運的是,從Laravel 5.1.11開始,Laravel提供了開箱即用的授權功能用於實現上述需求,我們不再需要做任何額外工作,用就是了。
附註:在開始本節之前,請參考升級指南將Laravel升級到Laravel 5.1.11,否則無法實現相關功能。
2、能做什麼?
Laravel提供的開箱即用的ACL被稱為Gate(這並不是類似Spark的產品名稱,而只是一個類別和門面的名稱)。
使用Gate類別(注入或使用Gate門面)讓我們可以輕鬆檢查某個使用者(目前登入使用者或指定使用者)是否允許操作特定事物。檢查程式碼如下:
if (Gate::denies('update-post', $post)) { abort(403); }
將這段程式碼放到控制器中,它將會使用定義好的規則update-post來檢查目前認證使用者是否有權限更新指定文章。
你也可以使用Gate::allows,該方法與denies方法相對,還可以在Blade視圖模板中透過@can來使用,還有更多更多,讓我們接下來一窺究竟。
3、如何使用?
Laravel ACL建立在「權限」概念之上,權限包含一個鍵(例如update-post)和一個傳回true或false的閉包(可傳入參數)。
3.1 定義權限
讓我們在AuthServiceProvider中定義使用者更新文章權限update-post如下:
<?php namespace App\Providers; use Illuminate\Contracts\Auth\Access\Gate as GateContract; use Illuminate\Foundation\Support\Providers\AuthServiceProvider as ServiceProvider; class AuthServiceProvider extends ServiceProvider{ /** * 注册应用所有的认证/授权服务. * * @param \Illuminate\Contracts\Auth\Access\Gate $gate * @return void */ public function boot(GateContract $gate) { parent::registerPolicies($gate); $gate->define('update-post', function ($user, $post) { return $user->id === $post->user_id; }); } }
正如你所看到的,定義權限閉包的第一個參數是指定用戶,如果目前用戶沒有通過登入認證,Gate將會自定傳回false。
當然,除了閉包之外,還可以透過類別方法作為第二個參數來取代閉包,該類別會在容器中解析:
$gate->define( 'update-post', 'PostPolicy@update');
#3.2 透過Gate門面檢查權限
Gate提供以下方法進行權限檢查:check、 allows和denies,check和allows功能用法完全一樣,而denies和allows功能相反。
如果你使用門面檢查權限,則不需要傳遞使用者實例,Gate門面會自動將目前使用者傳遞進來:
if (Gate::denies('update-post', $post)) { abort(403); }
如果你在權限中定義了多個參數:
Gate::define('delete-comment', function ($user, $post, $comment) { // });
則檢查方法如下:
if (Gate::allows('delete-comment', [$post, $comment])) { // }
如果你想檢查非目前認證使用者是否有權限操作,呼叫方法如下:
if (Gate::forUser($user)->allows('update-post', $post)) { // }
3.3 使用Gate注入檢查權限
和以往一樣,可以注入Gate類別而不是使用其門面,注入的類別和你在AuthServiceProvider中一樣-Illuminate\Contracts\Auth\Access\Gate:
public function somethingResolvedFromContainer(Gate $gate) { if ($gate->denies('update-post')) { // etc. } }
3.4 使用User模型檢查權限
Laravel的App\User模型現在使用了Authorizabletrait,因此可以使用其提供的can和cannot方法,分別對應Gate的allows和denies方法。
所以我們也可以使用User模型來檢查權限:
public function update(Request $request, $id) { $post = Post::findOrFail($id); if ($request->user()->cannot('update-post', $post)) { abort(403); } // 更新文章... }
3.5 在Blade中檢查權限
你可以在Blade中使用@can指令來檢查權限:
<a href="/post/{{ $post->id }}">查看文章</a> @can('update-post', $post) <a href="/post/{{ $post->id }}/edit">编辑文章</a> @endcan
與之相對的是@else指令:
@can('update-post', $post) <!-- The Current User Can Update The Post --> @else <!-- The Current User Can't Update The Post --> @endcan
3.6 中止權限檢查
如果是管理員或超級使用者擁有所有權限怎麼做?或者你想要為使用者臨時切換ACL邏輯又該如何?
Gate提供的before方法允許你在一些特定情況下在執行其他檢查之前就返回,不再往下檢查權限:
$gate->before(function ($user, $ability) { if ($user->last_name === 'Stauffer') { return true; } });
或使用使用者自己的時候:
$gate->before(function ($user, $ability) { if ($user->isOwner()) { return true; } });
3.7 策略類別
隨著應用程式邏輯越來越複雜,要處理的權限越來越多,將所有權限定義在AuthServiceProvider顯然不是一個明智的做法,因此Laravel引入了策略類,策略類是一些原生的PHP類,和控制器基於資源對路由進行分組類似,策略類基於資源對權限進行分組管理。
產生策略類別
可以使用以下Artisan指令產生PostPolicy策略類別:
php artisan make:policy PostPolicy
產生的策略類別位於app/Policies目錄。
然後我們可以在AuthServiceProvider的policies屬性中註冊策略類別:
protected $policies = [ Post::class => PostPolicy::class, ];
下面我們編輯PostPolicy如下:
<?php namespace App\Policies; use App\User; use App\Post; class PostPolicy{ /** * 判断给定文章是否可以被给定用户更新 * * @param \App\User $user * @param \App\Post $post * @return bool */ public function update(User $user, Post $post) { return $user->id === $post->user_id; } }
註:所有策略類別都透過服務容器進行解析,這意味著你可以在策略類別的建構函式中類型提示任何依賴,它們將會自動被注入。
檢查策略
如果为某个资源类型定义了策略类,Gate将会使用第一个参数来判断检查策略类上的哪个方法。
因此,要检查是否有权限更新某篇文章,只需要传入文章实例和update权限:
<?php namespace App\Http\Controllers; use Gate; use App\User; use App\Post; use App\Http\Controllers\Controller; class PostController extends Controller{ /** * 更新给定文章 * * @param int $id * @return Response */ public function update($id) { $post = Post::findOrFail($id); if (Gate::denies('update', $post)) { abort(403); } // 更新文章... } }
当然也可以使用User模型和Blade指令检查权限。
此外,Laravel还提供了一个全局帮助函数policy来检查权限:
if (policy($post)->update($user, $post)) { // }
3.8 控制器授权
由于大多数授权都会在检查权限失败的情况下退出控制器方法,因此在控制器中检查权限有一条捷径(AuthorizesRequeststrait提供,该trait在基类控制器Controller中被使用):
<?php namespace App\Http\Controllers; use App\Post; use App\Http\Controllers\Controller; class PostController extends Controller{ /** * 更新给定文章 * * @param int $id * @return Response */ public function update($id) { $post = Post::findOrFail($id); $this->authorize('update', $post); // 更新文章... } }
和我们上面的例子一样,如果授权失败会抛出403错误。
最后,如果你的控制器方法名和策略类中的方法名相同,例如都是update,则可以省略authorize的第一个参数:
public function update($id){ $post = Post::findOrFail($id); $this->authorize($post); // 更新文章... }
此外,AuthorizesRequests也提供了对非当前认证用户权限检查的支持:
$this->authorizeForUser($user, 'update', $post);
相关文章推荐:
Laravel 5.1框架中如何创建自定义Artisan控制台命令
相关课程推荐:
以上是Laravel 5.1框架中的ACL使用者授權與權限檢查功能的實現的詳細內容。更多資訊請關注PHP中文網其他相關文章!