<article><h1>Laravel 的用戶授權系統</h1><ul><li><a href="#introduction">簡介</a></li><li><a href="#gates">Gates</a><ul><li><a href="#writing-gates">編寫 Gates</a></li><li><a href="#authorizing-actions-via-gates">通過 Gates 授權動作</a></li></ul></li><li><a href="#creating-policies">創建策略</a><ul><li><a href="#generating-policies">生成策略</a></li><li><a href="#registering-policies">注冊策略</a></li></ul></li><li><a href="#writing-policies">編寫策略</a><ul><li><a href="#policy-methods">策略方法</a></li><li><a href="#methods-without-models">不使用模型方法</a></li><li><a href="#policy-filters">策略過濾器</a></li></ul></li><li><a href="#authorizing-actions-using-policies">使用策略授權動作</a><ul><li><a href="#via-the-user-model">通過用戶模型</a></li><li><a href="#via-middleware">通過中間件</a></li><li><a href="#via-controller-helpers">通過控制器輔助函數</a></li><li><a href="#via-blade-templates">通過 Blade 模板</a></li></ul></li></ul><p><a name="introduction"></a></p><h2><a href="#introduction">簡介</a></h2><p>除了內置提供的 <a href="/docs/5.4/authentication">用戶認證</a> 服務外,Laravel 還提供一種更簡單的方式來處理用戶授權動作。類似用戶認證,Laravel 有 2 種主要方式來實現用戶授權:gates 和策略。</p><p>可以把 gates 和策略類比于路由和控制器。Gates 提供了一個簡單、基于閉包的方式來授權認證。策略則和控制器類似,在特定的模型或者資源中通過分組來實現授權認證的邏輯。我們先來看看 gates,然后再看策略。</p><p>在你的應用中,不要將 gates 和策略當作相互排斥的方式。大部分應用很可能同時包含 gates 和策略,并且能很好的工作。Gates 大部分應用在模型和資源無關的地方,比如查看管理員的面板。與此相比,策略應該用在特定的模型或者資源中。</p><p><a name="gates"></a></p><h2><a href="#gates">Gates</a></h2><p><a name="writing-gates"></a></p><h3>編寫 Gates</h3><p>Gates 是用來決定用戶是否授權訪問給定的動作的閉包函數,并且典型的做法是在 <code class=" language-php">App\<span class="token package">Providers<span class="token punctuation">\</span>AuthServiceProvider</span></code> 類中使用 <code class=" language-php">Gate</code> facade 定義。Gates 接受一個用戶實例作為第一個參數,接受可選參數,比如相關的 Eloquent 模型:</p><pre class=" language-php"><code class=" language-php"><span class="token comment" spellcheck="true">/**
* 注冊任意用戶認證 、用戶授權服務。
*
* @return void
*/</span>
<span class="token keyword">public</span> <span class="token keyword">function</span> <span class="token function">boot<span class="token punctuation">(</span></span><span class="token punctuation">)</span>
<span class="token punctuation">{</span>
<span class="token this">$this</span><span class="token operator">-</span><span class="token operator">></span><span class="token function">registerPolicies<span class="token punctuation">(</span></span><span class="token punctuation">)</span><span class="token punctuation">;</span>
<span class="token scope">Gate<span class="token punctuation">::</span></span><span class="token function">define<span class="token punctuation">(</span></span><span class="token string">'update-post'</span><span class="token punctuation">,</span> <span class="token keyword">function</span> <span class="token punctuation">(</span><span class="token variable">$user</span><span class="token punctuation">,</span> <span class="token variable">$post</span><span class="token punctuation">)</span> <span class="token punctuation">{</span>
<span class="token keyword">return</span> <span class="token variable">$user</span><span class="token operator">-</span><span class="token operator">></span><span class="token property">id</span> <span class="token operator">==</span> <span class="token variable">$post</span><span class="token operator">-</span><span class="token operator">></span><span class="token property">user_id</span><span class="token punctuation">;</span>
<span class="token punctuation">}</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
<span class="token punctuation">}</span></code></pre><p><a name="authorizing-actions-via-gates"></a></p><h3>使用 gates 授權動作</h3><p>使用 gates 來授權動作時,應當使用 <code class=" language-php">allows</code> 方法。注意你并不需要傳遞當前認證通過的用戶給 <code class=" language-php">allows</code> 方法。Laravel 會自動處理好傳入的用戶,然后傳遞給 gate 閉包函數:</p><pre class=" language-php"><code class=" language-php"><span class="token keyword">if</span> <span class="token punctuation">(</span><span class="token scope">Gate<span class="token punctuation">::</span></span><span class="token function">allows<span class="token punctuation">(</span></span><span class="token string">'update-post'</span><span class="token punctuation">,</span> <span class="token variable">$post</span><span class="token punctuation">)</span><span class="token punctuation">)</span> <span class="token punctuation">{</span>
<span class="token comment" spellcheck="true"> // 當前用戶可以更新 post...
</span><span class="token punctuation">}</span>
<span class="token keyword">if</span> <span class="token punctuation">(</span><span class="token scope">Gate<span class="token punctuation">::</span></span><span class="token function">denies<span class="token punctuation">(</span></span><span class="token string">'update-post'</span><span class="token punctuation">,</span> <span class="token variable">$post</span><span class="token punctuation">)</span><span class="token punctuation">)</span> <span class="token punctuation">{</span>
<span class="token comment" spellcheck="true"> // 當前用戶不能更新 post...
</span><span class="token punctuation">}</span></code></pre><p>如果需要指定一個特定的用戶可以訪問某個動作,可以使用 <code class=" language-php">Gate</code> facade 中的 <code class=" language-php">forUser</code> 方法:</p><pre class=" language-php"><code class=" language-php"><span class="token keyword">if</span> <span class="token punctuation">(</span><span class="token scope">Gate<span class="token punctuation">::</span></span><span class="token function">forUser<span class="token punctuation">(</span></span><span class="token variable">$user</span><span class="token punctuation">)</span><span class="token operator">-</span><span class="token operator">></span><span class="token function">allows<span class="token punctuation">(</span></span><span class="token string">'update-post'</span><span class="token punctuation">,</span> <span class="token variable">$post</span><span class="token punctuation">)</span><span class="token punctuation">)</span> <span class="token punctuation">{</span>
<span class="token comment" spellcheck="true"> // 指定用戶可以更新 post...
</span><span class="token punctuation">}</span>
<span class="token keyword">if</span> <span class="token punctuation">(</span><span class="token scope">Gate<span class="token punctuation">::</span></span><span class="token function">forUser<span class="token punctuation">(</span></span><span class="token variable">$user</span><span class="token punctuation">)</span><span class="token operator">-</span><span class="token operator">></span><span class="token function">denies<span class="token punctuation">(</span></span><span class="token string">'update-post'</span><span class="token punctuation">,</span> <span class="token variable">$post</span><span class="token punctuation">)</span><span class="token punctuation">)</span> <span class="token punctuation">{</span>
<span class="token comment" spellcheck="true"> // 指定用戶不能更新 post...
</span><span class="token punctuation">}</span></code></pre><p><a name="creating-policies"></a></p><h2><a href="#creating-policies">創建策略</a></h2><p><a name="generating-policies"></a></p><h3>生成策略</h3><p>策略是在特定模型或者資源中組織授權邏輯的類。例如,如果應用是一個博客,會有一個 <code class=" language-php">Post</code> 模型和一個相應的 <code class=" language-php">PostPolicy</code> 來授權用戶動作,比如創建或者更新博客。</p><p>可以使用 <code class=" language-php">make<span class="token punctuation">:</span>policy</code> <a href="/docs/5.4/artisan">artisan 命令</a> 來生成策略。生成的策略將放置在 <code class=" language-php">app<span class="token operator">/</span>Policies</code> 目錄。如果在你的應用中不存在這個目錄,那么 Laravel 會自動創建:</p><pre class=" language-php"><code class=" language-php">php artisan make<span class="token punctuation">:</span>policy PostPolicy</code></pre><p><code class=" language-php">make<span class="token punctuation">:</span>policy</code> 會生成空的策略類。如果希望生成的類包含基本的「CRUD」策略方法, 可以在使用命令時指定 <code class=" language-php"><span class="token operator">--</span>model</code> 選項:</p><pre class=" language-php"><code class=" language-php">php artisan make<span class="token punctuation">:</span>policy PostPolicy <span class="token operator">--</span>model<span class="token operator">=</span>Post</code></pre><blockquote class="has-icon tip"><p><div class="flag"><span class="svg"><svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" xmlns:a="http://ns.adobe.com/AdobeSVGViewerExtensions/3.0/" version="1.1" x="0px" y="0px" width="56.6px" height="87.5px" viewBox="0 0 56.6 87.5" enable-background="new 0 0 56.6 87.5" xml:space="preserve"><path fill="#FFFFFF" d="M28.7 64.5c-1.4 0-2.5-1.1-2.5-2.5v-5.7 -5V41c0-1.4 1.1-2.5 2.5-2.5s2.5 1.1 2.5 2.5v10.1 5 5.8C31.2 63.4 30.1 64.5 28.7 64.5zM26.4 0.1C11.9 1 0.3 13.1 0 27.7c-0.1 7.9 3 15.2 8.2 20.4 0.5 0.5 0.8 1 1 1.7l3.1 13.1c0.3 1.1 1.3 1.9 2.4 1.9 0.3 0 0.7-0.1 1.1-0.2 1.1-0.5 1.6-1.8 1.4-3l-2-8.4 -0.4-1.8c-0.7-2.9-2-5.7-4-8 -1-1.2-2-2.5-2.7-3.9C5.8 35.3 4.7 30.3 5.4 25 6.7 14.5 15.2 6.3 25.6 5.1c13.9-1.5 25.8 9.4 25.8 23 0 4.1-1.1 7.9-2.9 11.2 -0.8 1.4-1.7 2.7-2.7 3.9 -2 2.3-3.3 5-4 8L41.4 53l-2 8.4c-0.3 1.2 0.3 2.5 1.4 3 0.3 0.2 0.7 0.2 1.1 0.2 1.1 0 2.2-0.8 2.4-1.9l3.1-13.1c0.2-0.6 0.5-1.2 1-1.7 5-5.1 8.2-12.1 8.2-19.8C56.4 12 42.8-1 26.4 0.1zM43.7 69.6c0 0.5-0.1 0.9-0.3 1.3 -0.4 0.8-0.7 1.6-0.9 2.5 -0.7 3-2 8.6-2 8.6 -1.3 3.2-4.4 5.5-7.9 5.5h-4.1H28h-0.5 -3.6c-3.5 0-6.7-2.4-7.9-5.7l-0.1-0.4 -1.8-7.8c-0.4-1.1-0.8-2.1-1.2-3.1 -0.1-0.3-0.2-0.5-0.2-0.9 0.1-1.3 1.3-2.1 2.6-2.1H41C42.4 67.5 43.6 68.2 43.7 69.6zM37.7 72.5H26.9c-4.2 0-7.2 3.9-6.3 7.9 0.6 1.3 1.8 2.1 3.2 2.1h4.1 0.5 0.5 3.6c1.4 0 2.7-0.8 3.2-2.1L37.7 72.5z"></path></svg></span></div> 所有授權策略會通過 Laravel <a href="/docs/5.4/container">服務容器</a> 解析,意指你可以在授權策略的構造器對任何需要的依賴使用類型提示,它們將會被自動注入。</p></blockquote><p><a name="registering-policies"></a></p><h3>注冊策略</h3><p>一旦該授權策略存在,需要將它進行注冊。<code class=" language-php">AuthServiceProvider</code> 包含了一個 <code class=" language-php">policies</code> 屬性,可將各種模型對應至管理它們的授權策略。注冊一個策略將引導 Laravel 在授權動作訪問給定模型時使用何種策略:</p><pre class=" language-php"><code class=" language-php"><span class="token delimiter"><?php</span>
<span class="token keyword">namespace</span> <span class="token package">App<span class="token punctuation">\</span>Providers</span><span class="token punctuation">;</span>
<span class="token keyword">use</span> <span class="token package">App<span class="token punctuation">\</span>Post</span><span class="token punctuation">;</span>
<span class="token keyword">use</span> <span class="token package">App<span class="token punctuation">\</span>Policies<span class="token punctuation">\</span>PostPolicy</span><span class="token punctuation">;</span>
<span class="token keyword">use</span> <span class="token package">Illuminate<span class="token punctuation">\</span>Support<span class="token punctuation">\</span>Facades<span class="token punctuation">\</span>Gate</span><span class="token punctuation">;</span>
<span class="token keyword">use</span> <span class="token package">Illuminate<span class="token punctuation">\</span>Foundation<span class="token punctuation">\</span>Support<span class="token punctuation">\</span>Providers<span class="token punctuation">\</span>AuthServiceProvider</span> <span class="token keyword">as</span> ServiceProvider<span class="token punctuation">;</span>
<span class="token keyword">class</span> <span class="token class-name">AuthServiceProvider</span> <span class="token keyword">extends</span> <span class="token class-name">ServiceProvider</span>
<span class="token punctuation">{</span>
<span class="token comment" spellcheck="true">/**
* 應用的策略映射。
*
* @var array
*/</span>
<span class="token keyword">protected</span> <span class="token variable">$policies</span> <span class="token operator">=</span> <span class="token punctuation">[</span>
<span class="token scope">Post<span class="token punctuation">::</span></span><span class="token keyword">class</span> <span class="token operator">=</span><span class="token operator">></span> <span class="token scope">PostPolicy<span class="token punctuation">::</span></span><span class="token keyword">class</span><span class="token punctuation">,</span>
<span class="token punctuation">]</span><span class="token punctuation">;</span>
<span class="token comment" spellcheck="true">/**
* 注冊任意用戶認證、用戶授權服務。
*
* @return void
*/</span>
<span class="token keyword">public</span> <span class="token keyword">function</span> <span class="token function">boot<span class="token punctuation">(</span></span><span class="token punctuation">)</span>
<span class="token punctuation">{</span>
<span class="token this">$this</span><span class="token operator">-</span><span class="token operator">></span><span class="token function">registerPolicies<span class="token punctuation">(</span></span><span class="token punctuation">)</span><span class="token punctuation">;</span>
<span class="token comment" spellcheck="true"> //
</span> <span class="token punctuation">}</span>
<span class="token punctuation">}</span></code></pre><p><a name="writing-policies"></a></p><h2><a href="#writing-policies">編寫策略</a></h2><p><a name="policy-methods"></a></p><h3>策略方法</h3><p>一旦授權策略被生成且注冊,我們就可以為每個權限的授權增加方法。例如,讓我們在 <code class=" language-php">PostPolicy</code> 中定義一個 <code class=" language-php">update</code> 方法,它會判斷指定的 <code class=" language-php">User</code> 是否可以「更新」一條 <code class=" language-php">Post</code>。</p><p><code class=" language-php">update</code> 方法接受 <code class=" language-php">User</code> 和 <code class=" language-php">Post</code> 實例作為參數,并且應當返回 <code class=" language-php"><span class="token boolean">true</span></code> 或 <code class=" language-php"><span class="token boolean">false</span></code> 來指明用戶是否授權更新給定的 <code class=" language-php">Post</code>。因此,這個例子中,我們判斷用戶的 id 是否和 post 中的 <code class=" language-php">user_id</code> 匹配:</p><pre class=" language-php"><code class=" language-php"><span class="token delimiter"><?php</span>
<span class="token keyword">namespace</span> <span class="token package">App<span class="token punctuation">\</span>Policies</span><span class="token punctuation">;</span>
<span class="token keyword">use</span> <span class="token package">App<span class="token punctuation">\</span>User</span><span class="token punctuation">;</span>
<span class="token keyword">use</span> <span class="token package">App<span class="token punctuation">\</span>Post</span><span class="token punctuation">;</span>
<span class="token keyword">class</span> <span class="token class-name">PostPolicy</span>
<span class="token punctuation">{</span>
<span class="token comment" spellcheck="true">/**
* 判斷給定博客能否被用戶更新
*
* @param \App\User $user
* @param \App\Post $post
* @return bool
*/</span>
<span class="token keyword">public</span> <span class="token keyword">function</span> <span class="token function">update<span class="token punctuation">(</span></span>User <span class="token variable">$user</span><span class="token punctuation">,</span> Post <span class="token variable">$post</span><span class="token punctuation">)</span>
<span class="token punctuation">{</span>
<span class="token keyword">return</span> <span class="token variable">$user</span><span class="token operator">-</span><span class="token operator">></span><span class="token property">id</span> <span class="token operator">===</span> <span class="token variable">$post</span><span class="token operator">-</span><span class="token operator">></span><span class="token property">user_id</span><span class="token punctuation">;</span>
<span class="token punctuation">}</span>
<span class="token punctuation">}</span></code></pre><p>你可以接著在此授權策略定義額外的方法,作為各種權限需要的授權。例如,你可以定義 <code class=" language-php">view</code> 或 <code class=" language-php">delete</code> 方法來授權 <code class=" language-php">Post</code> 的多種行為。可以為自定義策略方法使用自己喜歡的名字。</p><blockquote class="has-icon tip"><p><div class="flag"><span class="svg"><svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" xmlns:a="http://ns.adobe.com/AdobeSVGViewerExtensions/3.0/" version="1.1" x="0px" y="0px" width="56.6px" height="87.5px" viewBox="0 0 56.6 87.5" enable-background="new 0 0 56.6 87.5" xml:space="preserve"><path fill="#FFFFFF" d="M28.7 64.5c-1.4 0-2.5-1.1-2.5-2.5v-5.7 -5V41c0-1.4 1.1-2.5 2.5-2.5s2.5 1.1 2.5 2.5v10.1 5 5.8C31.2 63.4 30.1 64.5 28.7 64.5zM26.4 0.1C11.9 1 0.3 13.1 0 27.7c-0.1 7.9 3 15.2 8.2 20.4 0.5 0.5 0.8 1 1 1.7l3.1 13.1c0.3 1.1 1.3 1.9 2.4 1.9 0.3 0 0.7-0.1 1.1-0.2 1.1-0.5 1.6-1.8 1.4-3l-2-8.4 -0.4-1.8c-0.7-2.9-2-5.7-4-8 -1-1.2-2-2.5-2.7-3.9C5.8 35.3 4.7 30.3 5.4 25 6.7 14.5 15.2 6.3 25.6 5.1c13.9-1.5 25.8 9.4 25.8 23 0 4.1-1.1 7.9-2.9 11.2 -0.8 1.4-1.7 2.7-2.7 3.9 -2 2.3-3.3 5-4 8L41.4 53l-2 8.4c-0.3 1.2 0.3 2.5 1.4 3 0.3 0.2 0.7 0.2 1.1 0.2 1.1 0 2.2-0.8 2.4-1.9l3.1-13.1c0.2-0.6 0.5-1.2 1-1.7 5-5.1 8.2-12.1 8.2-19.8C56.4 12 42.8-1 26.4 0.1zM43.7 69.6c0 0.5-0.1 0.9-0.3 1.3 -0.4 0.8-0.7 1.6-0.9 2.5 -0.7 3-2 8.6-2 8.6 -1.3 3.2-4.4 5.5-7.9 5.5h-4.1H28h-0.5 -3.6c-3.5 0-6.7-2.4-7.9-5.7l-0.1-0.4 -1.8-7.8c-0.4-1.1-0.8-2.1-1.2-3.1 -0.1-0.3-0.2-0.5-0.2-0.9 0.1-1.3 1.3-2.1 2.6-2.1H41C42.4 67.5 43.6 68.2 43.7 69.6zM37.7 72.5H26.9c-4.2 0-7.2 3.9-6.3 7.9 0.6 1.3 1.8 2.1 3.2 2.1h4.1 0.5 0.5 3.6c1.4 0 2.7-0.8 3.2-2.1L37.7 72.5z"></path></svg></span></div> 如果在 Artisan 控制臺生成策略使用 <code class=" language-php"><span class="token operator">--</span>model</code> 選項,會自動包含 <code class=" language-php">view</code>、<code class=" language-php">create</code>、<code class=" language-php">update</code> 和 <code class=" language-php">delete</code> 動作。</p></blockquote><p><a name="methods-without-models"></a></p><h3>不包含模型方法</h3><p>一些策略方法只接受當前認證通過的用戶作為參數,而不用傳入授權相關的模型實例。最普遍的應用場景就是授權 <code class=" language-php">create</code> 動作。例如,如果正在創建一篇博客,你可能希望檢查一下當前用戶是否授權創建博客。</p><p>當定義一個不需要傳入模型實例的策略方法時,比如 <code class=" language-php">create</code> 方法,你需要定義這個方法只接受已授權的用戶作為參數:</p><pre class=" language-php"><code class=" language-php"><span class="token comment" spellcheck="true">/**
* 判斷給定用戶是否可以創建博客。
*
* @param \App\User $user
* @return bool
*/</span>
<span class="token keyword">public</span> <span class="token keyword">function</span> <span class="token function">create<span class="token punctuation">(</span></span>User <span class="token variable">$user</span><span class="token punctuation">)</span>
<span class="token punctuation">{</span>
<span class="token comment" spellcheck="true"> //
</span><span class="token punctuation">}</span></code></pre><p><a name="policy-filters"></a></p><h3>策略過濾器</h3><p>對特定用戶,你可能希望通過指定的策略授權所有動作。要達到這個目的,可以在策略中定義一個 <code class=" language-php">before</code> 方法。<code class=" language-php">before</code> 方法會在策略中其他所有方法之前執行,這樣提供了一種方式來授權動作而不是指定的策略方法來執行判斷。這個功能最常見的場景是授權應用的管理員可以訪問所有動作:</p><pre class=" language-php"><code class=" language-php"><span class="token keyword">public</span> <span class="token keyword">function</span> <span class="token function">before<span class="token punctuation">(</span></span><span class="token variable">$user</span><span class="token punctuation">,</span> <span class="token variable">$ability</span><span class="token punctuation">)</span>
<span class="token punctuation">{</span>
<span class="token keyword">if</span> <span class="token punctuation">(</span><span class="token variable">$user</span><span class="token operator">-</span><span class="token operator">></span><span class="token function">isSuperAdmin<span class="token punctuation">(</span></span><span class="token punctuation">)</span><span class="token punctuation">)</span> <span class="token punctuation">{</span>
<span class="token keyword">return</span> <span class="token boolean">true</span><span class="token punctuation">;</span>
<span class="token punctuation">}</span>
<span class="token punctuation">}</span></code></pre><p>如果你想拒絕用戶所有的授權,你應該在 <code class=" language-php">before</code> 方法中返回 <code class=" language-php"><span class="token boolean">false</span></code>。如果返回的是 <code class=" language-php"><span class="token keyword">null</span></code>,則通過其他的策略方法來決定授權與否。</p><p><a name="authorizing-actions-using-policies"></a></p><h2><a href="#authorizing-actions-using-policies">使用策略授權動作</a></h2><p><a name="via-the-user-model"></a></p><h3>通過用戶模型</h3><p>Laravel 應用內置的 <code class=" language-php">User</code> 模型包含 2 個有用的方法來授權動作:<code class=" language-php">can</code> 和 <code class=" language-php">cant</code>。<code class=" language-php">can</code> 方法指定需要授權的動作和相關的模型。例如,判定一個用戶是否授權更新給定的 <code class=" language-php">Post</code> 模型:</p><pre class=" language-php"><code class=" language-php"><span class="token keyword">if</span> <span class="token punctuation">(</span><span class="token variable">$user</span><span class="token operator">-</span><span class="token operator">></span><span class="token function">can<span class="token punctuation">(</span></span><span class="token string">'update'</span><span class="token punctuation">,</span> <span class="token variable">$post</span><span class="token punctuation">)</span><span class="token punctuation">)</span> <span class="token punctuation">{</span>
<span class="token comment" spellcheck="true"> //
</span><span class="token punctuation">}</span></code></pre><p>如果給定模型的 <a href="#registering-policies">策略已被注冊</a>,<code class=" language-php">can</code> 方法會自動調用核實的策略方法并且返回 boolean 值。如果沒有策略注冊到這個模型,<code class=" language-php">can</code> 方法會嘗試調用和動作名相匹配的基于閉包的 Gate。</p><h4>不需要指定模型的動作</h4><p>一些動作,比如 <code class=" language-php">create</code>,并不需要指定模型實例。在這種情況下,可傳遞一個類名給 <code class=" language-php">can</code> 方法。當授權動作時,這個類名將被用來判斷使用哪個策略:</p><pre class=" language-php"><code class=" language-php"><span class="token keyword">use</span> <span class="token package">App<span class="token punctuation">\</span>Post</span><span class="token punctuation">;</span>
<span class="token keyword">if</span> <span class="token punctuation">(</span><span class="token variable">$user</span><span class="token operator">-</span><span class="token operator">></span><span class="token function">can<span class="token punctuation">(</span></span><span class="token string">'create'</span><span class="token punctuation">,</span> <span class="token scope">Post<span class="token punctuation">::</span></span><span class="token keyword">class</span><span class="token punctuation">)</span><span class="token punctuation">)</span> <span class="token punctuation">{</span>
<span class="token comment" spellcheck="true"> // 執行相關策略中的「create」方法...
</span><span class="token punctuation">}</span></code></pre><p><a name="via-middleware"></a></p><h3>通過中間件</h3><p>Laravel 包含一個可以在請求到達路由或控制器之前就進行動作授權的中間件。默認,<code class=" language-php">Illuminate\<span class="token package">Auth<span class="token punctuation">\</span>Middleware<span class="token punctuation">\</span>Authorize</span></code> 中間件被指定到 <code class=" language-php">App\<span class="token package">Http<span class="token punctuation">\</span>Kernel</span></code> 類中 <code class=" language-php">can</code> 鍵上。我們用一個授權用戶更新博客的例子來講解 <code class=" language-php">can</code> 中間件的使用:</p><pre class=" language-php"><code class=" language-php"><span class="token keyword">use</span> <span class="token package">App<span class="token punctuation">\</span>Post</span><span class="token punctuation">;</span>
<span class="token scope">Route<span class="token punctuation">::</span></span><span class="token function">put<span class="token punctuation">(</span></span><span class="token string">'/post/{post}'</span><span class="token punctuation">,</span> <span class="token keyword">function</span> <span class="token punctuation">(</span>Post <span class="token variable">$post</span><span class="token punctuation">)</span> <span class="token punctuation">{</span>
<span class="token comment" spellcheck="true"> // 當前用戶可以更新博客...
</span><span class="token punctuation">}</span><span class="token punctuation">)</span><span class="token operator">-</span><span class="token operator">></span><span class="token function">middleware<span class="token punctuation">(</span></span><span class="token string">'can:update,post'</span><span class="token punctuation">)</span><span class="token punctuation">;</span></code></pre><p>在這個例子中,我們傳遞給 <code class=" language-php">can</code> 中間件 2 個參數。第一個是需要授權的動作的名稱,第二個是我們希望傳遞給策略方法的路由參數。這里因為使用了 <a href="/docs/5.4/routing#implicit-binding">隱式模型綁定</a>,一個 <code class=" language-php">Post</code> 會被傳遞給策略方法。如果用戶不被授權訪問指定的動作,這個中間件會生成帶有 <code class=" language-php"><span class="token number">403</span></code> 狀態碼的 HTTP 響應。</p><h4>不需要指定模型的動作</h4><p>同樣的,一些動作,比如 <code class=" language-php">create</code>,并不需要指定模型實例。在這種情況下,可傳遞一個類名給中間件。當授權動作時,這個類名將被用來判斷使用哪個策略:</p><pre class=" language-php"><code class=" language-php"><span class="token scope">Route<span class="token punctuation">::</span></span><span class="token function">post<span class="token punctuation">(</span></span><span class="token string">'/post'</span><span class="token punctuation">,</span> <span class="token keyword">function</span> <span class="token punctuation">(</span><span class="token punctuation">)</span> <span class="token punctuation">{</span>
<span class="token comment" spellcheck="true"> // 當前用戶可以創建博客...
</span><span class="token punctuation">}</span><span class="token punctuation">)</span><span class="token operator">-</span><span class="token operator">></span><span class="token function">middleware<span class="token punctuation">(</span></span><span class="token string">'can:create,App\Post'</span><span class="token punctuation">)</span><span class="token punctuation">;</span></code></pre><p><a name="via-controller-helpers"></a></p><h3>通過控制器輔助函數</h3><p>除了在 <code class=" language-php">User</code> 模型中提供輔助方法外,Laravel 也為所有繼承了 <code class=" language-php">App\<span class="token package">Http<span class="token punctuation">\</span>Controllers<span class="token punctuation">\</span>Controller</span></code> 基類的控制器提供了一個有用的 <code class=" language-php">authorize</code> 方法。和 <code class=" language-php">can</code> 方法類似,這個方法接收需要授權的動作和相關的模型作為參數。如果動作不被授權,<code class=" language-php">authorize</code> 方法會拋出 <code class=" language-php">Illuminate\<span class="token package">Auth<span class="token punctuation">\</span>Access<span class="token punctuation">\</span>AuthorizationException</span></code> 異常,然后被 Laravel 默認的異常處理器轉化為帶有 <code class=" language-php"><span class="token number">403</span></code> 狀態碼的 HTTP 響應:</p><pre class=" language-php"><code class=" language-php"><span class="token delimiter"><?php</span>
<span class="token keyword">namespace</span> <span class="token package">App<span class="token punctuation">\</span>Http<span class="token punctuation">\</span>Controllers</span><span class="token punctuation">;</span>
<span class="token keyword">use</span> <span class="token package">App<span class="token punctuation">\</span>Post</span><span class="token punctuation">;</span>
<span class="token keyword">use</span> <span class="token package">Illuminate<span class="token punctuation">\</span>Http<span class="token punctuation">\</span>Request</span><span class="token punctuation">;</span>
<span class="token keyword">use</span> <span class="token package">App<span class="token punctuation">\</span>Http<span class="token punctuation">\</span>Controllers<span class="token punctuation">\</span>Controller</span><span class="token punctuation">;</span>
<span class="token keyword">class</span> <span class="token class-name">PostController</span> <span class="token keyword">extends</span> <span class="token class-name">Controller</span>
<span class="token punctuation">{</span>
<span class="token comment" spellcheck="true">/**
* 更新給定博客
*
* @param Request $request
* @param Post $post
* @return Response
*/</span>
<span class="token keyword">public</span> <span class="token keyword">function</span> <span class="token function">update<span class="token punctuation">(</span></span>Request <span class="token variable">$request</span><span class="token punctuation">,</span> Post <span class="token variable">$post</span><span class="token punctuation">)</span>
<span class="token punctuation">{</span>
<span class="token this">$this</span><span class="token operator">-</span><span class="token operator">></span><span class="token function">authorize<span class="token punctuation">(</span></span><span class="token string">'update'</span><span class="token punctuation">,</span> <span class="token variable">$post</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
<span class="token comment" spellcheck="true"> // 當前用戶可以更新博客...
</span> <span class="token punctuation">}</span>
<span class="token punctuation">}</span></code></pre><h4>不需要指定模型的動作</h4><p>和之前討論的一樣,一些動作,比如 <code class=" language-php">create</code>,并不需要指定模型實例。在這種情況下,可傳遞一個類名給 <code class=" language-php">authorize</code> 方法。當授權動作時,這個類名將被用來判斷使用哪個策略:</p><pre class=" language-php"><code class=" language-php"><span class="token comment" spellcheck="true">/**
* 新建博客
*
* @param Request $request
* @return Response
*/</span>
<span class="token keyword">public</span> <span class="token keyword">function</span> <span class="token function">create<span class="token punctuation">(</span></span>Request <span class="token variable">$request</span><span class="token punctuation">)</span>
<span class="token punctuation">{</span>
<span class="token this">$this</span><span class="token operator">-</span><span class="token operator">></span><span class="token function">authorize<span class="token punctuation">(</span></span><span class="token string">'create'</span><span class="token punctuation">,</span> <span class="token scope">Post<span class="token punctuation">::</span></span><span class="token keyword">class</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
<span class="token comment" spellcheck="true"> // 當前用戶可以新建博客...
</span><span class="token punctuation">}</span></code></pre><p><a name="via-blade-templates"></a></p><h3>通過 Blade 模板</h3><p>當編寫 Blade 模板時,你可能希望頁面的指定部分只展示給允許授權訪問給定動作的用戶。例如,你可能希望只展示更新表單給有權更新博客的用戶。這種情況下,你可以直接使用 <code class=" language-php">@can</code> 和 <code class=" language-php">@cannot</code> 指令。</p><pre class=" language-php"><code class=" language-php">@<span class="token function">can<span class="token punctuation">(</span></span><span class="token string">'update'</span><span class="token punctuation">,</span> <span class="token variable">$post</span><span class="token punctuation">)</span>
<span class="token markup"><span class="token comment" spellcheck="true"><!-- 當前用戶可以更新博客 --></span></span>
@<span class="token function">elsecan<span class="token punctuation">(</span></span><span class="token string">'create'</span><span class="token punctuation">,</span> <span class="token variable">$post</span><span class="token punctuation">)</span>
<span class="token markup"><span class="token comment" spellcheck="true"><!-- 當前用戶可以新建博客 --></span></span>
@endcan
@<span class="token function">cannot<span class="token punctuation">(</span></span><span class="token string">'update'</span><span class="token punctuation">,</span> <span class="token variable">$post</span><span class="token punctuation">)</span>
<span class="token markup"><span class="token comment" spellcheck="true"><!-- 當前用戶不可以更新博客 --></span></span>
@<span class="token function">elsecannot<span class="token punctuation">(</span></span><span class="token string">'create'</span><span class="token punctuation">,</span> <span class="token variable">$post</span><span class="token punctuation">)</span>
<span class="token markup"><span class="token comment" spellcheck="true"><!-- 當前用戶不可以新建博客 --></span></span>
@endcannot</code></pre><p>這些指令在編寫 <code class=" language-php">@<span class="token keyword">if</span></code> 和 <code class=" language-php">@unless</code> 時提供了方便的縮寫。<code class=" language-php">@can</code> 和 <code class=" language-php">@cannot</code> 各自轉化為如下聲明:</p><pre class=" language-php"><code class=" language-php">@<span class="token keyword">if</span> <span class="token punctuation">(</span><span class="token scope">Auth<span class="token punctuation">::</span></span><span class="token function">user<span class="token punctuation">(</span></span><span class="token punctuation">)</span><span class="token operator">-</span><span class="token operator">></span><span class="token function">can<span class="token punctuation">(</span></span><span class="token string">'update'</span><span class="token punctuation">,</span> <span class="token variable">$post</span><span class="token punctuation">)</span><span class="token punctuation">)</span>
<span class="token markup"><span class="token comment" spellcheck="true"><!-- 當前用戶可以更新博客 --></span></span>
@<span class="token keyword">endif</span>
@unless <span class="token punctuation">(</span><span class="token scope">Auth<span class="token punctuation">::</span></span><span class="token function">user<span class="token punctuation">(</span></span><span class="token punctuation">)</span><span class="token operator">-</span><span class="token operator">></span><span class="token function">can<span class="token punctuation">(</span></span><span class="token string">'update'</span><span class="token punctuation">,</span> <span class="token variable">$post</span><span class="token punctuation">)</span><span class="token punctuation">)</span>
<span class="token markup"><span class="token comment" spellcheck="true"><!-- 當前用戶不可以更新博客 --></span></span>
@endunless</code></pre><h4>不需要指定模型的動作</h4><p>和大部分其他的授權方法類似,當動作不需要模型實例時,你可以傳遞一個類名給 <code class=" language-php">@can</code> 和 <code class=" language-php">@cannot</code> 指令:</p><pre class=" language-php"><code class=" language-php">@<span class="token function">can<span class="token punctuation">(</span></span><span class="token string">'create'</span><span class="token punctuation">,</span> <span class="token scope">Post<span class="token punctuation">::</span></span><span class="token keyword">class</span><span class="token punctuation">)</span>
<span class="token markup"><span class="token comment" spellcheck="true"><!-- 當前用戶可以新建博客 --></span></span>
@endcan
@<span class="token function">cannot<span class="token punctuation">(</span></span><span class="token string">'create'</span><span class="token punctuation">,</span> <span class="token scope">Post<span class="token punctuation">::</span></span><span class="token keyword">class</span><span class="token punctuation">)</span>
<span class="token markup"><span class="token comment" spellcheck="true"><!-- 當前用戶不可以新建博客 --></span></span>
@endcannot</code></pre></article>
- 入門指南
- 安裝
- 配置信息
- 文件夾結構
- 請求周期
- 開發環境部署
- Valet
- Homestead
- 核心概念
- 服務提供者
- Facades
- Contracts
- 服務容器
- HTTP 層
- 路由
- 中間件
- CSRF 保護
- 控制器
- 請求
- 響應
- 視圖
- Session
- 表單驗證
- 前端
- Blade 模板
- 本地化
- 前端指南
- 編輯資源 Mix
- 安全
- API 認證
- 用戶認證
- 用戶授權
- 加密解密
- 哈希
- 重置密碼
- 數據庫
- 快速入門
- 查詢構造器
- 分頁
- 數據庫遷移
- Redis
- 數據填充
- Eloquent ORM
- Eloquent ORM快速入門
- 模型關聯
- Eloquent 集合
- 修改器
- 序列化
- 綜合話題
- Artisan 命令行
- 廣播系統
- 緩存系統
- 集合
- 錯誤與日志
- 事件系統
- 文件存儲
- 輔助函數
- 郵件發送
- 消息通知
- 擴展包開發
- 隊列
- 任務調度