Authorization

Giới thiệu

Ngoài việc cung cấp các service authentication, Laravel cũng cung cấp một cách đơn giản để tổ chức các logic cấp quyền và điều khiển việc truy cập vào tài nguyên. Như authentication, tiếp cận với ủy quyền của Laravel đơn giản và là hai cách chỉnh của hành động ủy quyền: gates và policies.

Có thể hiểu gates và policies như routes và controllers. Gates cung cấp một cách đơn giản, Closure based tiếp cận authorization trong policies, như controllers, nhóm các logicc xung quanh một particular model hoặc resource. Chúng ta sẽ khám phá gates trước khi tìm hiểu policies.

Điều quan trọng là không được xem gates và policies như là loại trừ lẫn nhau trong. Hầu hết các ứng dụng sẽ chứa hỗn hợp của gates và policies, và điều đó khá tốt! Gates hay được ạp dụn cho các hành động không liên quan đến model hoặc resource, như là xem một trang administrator dashboard. Ngược lại, policies được sử dụng khi bạn muốn authorize một hành động cụ thể của model hoặc resource.

Gates

Viết Gates

Gates are Closures xác định người dùng đã ủy quyền để thực hiện một hành động nhất định và thường được định nghĩa trong class App\Providers\AuthServiceProvider sử dụng Gate facade. Gates luôn luôn nhận một thể hiện của người dùng như là đố số đầu tiên, bạn có thể tùy biến nhận đối số bổ sung như là một relevant Eloquent model:

/**
 * Register any authentication / authorization services.
 *
 * @return  void
 */
public function boot()
{
    $this->registerPolicies();

    Gate::define('update-post', function ($user, $post) {
        return $user->id == $post->user_id;
    });
}

Ủy quyền hành động

Để ủy quyền một hành động sử dụng gates, bạn dùng phương thức allows. Chú ý là bạn không cần truyền chứng thực user hiện tại vào phương thức allows. Laravel sẽ tự động làm việc đó trong gate Closure:

if (Gate::allows('update-post', $post)) {
    // The current user can update the post...
}

Nếu bạn muốn xem một người dùng chứng thực cụ thể có được ủy wuyeenf một hành động, bạn có thể dùng phương thức forUser trong Gate facade:

if (Gate::forUser($user)->allows('update-post', $post)) {
    // The user can update the post...
}

Tạo mới Policies

Tạo ra Policies

Policies là các class tổ chức ủy quyền logic xung quanh model hoặc resource cụ thể. Ví dụ, nếu ứng dụng của bạn là blog, bạn có thể có model Post và tương ứng với nó là PostPolicy để ủy quyền người dùng như là được phép tạo mới hoặc update bài viết.

Bạn có thể tạo ra policy bằng lệnh make:policy artisan. policy dduwcoj tạo ra sẽ nằm ở thư mục app/Policies. Nếu thư mục không có trong ứng dụng, Laravel sẽ tạo nó giúp bạn:

php artisan make:policy PostPolicy

Lệnh make:policy sẽ tạo ra một class policy rỗng. Nếu bạn muốn tạo một class có đủ "CRUD" policy trong class, bạn có thể chỉ định --model khi thực thi lệnh:

php artisan make:policy PostPolicy --model=Post

Tất cả policies đều được xử lý qua Laravel service container, cho phép bạn type-hint bất cứ dependencies cần thiết trong hàm khởi tạo constructor của prolicy để chúng tự động được injected.

Đăng ký Policies

Khi policy đã tồn tại, nó cần được đăng ký. Class AuthServiceProvider đã có sẵn trong ứng dụng Laravel chứa một thuộc tính policies nó sẽ map với Eloquent models tương ứng với policies. Đăng ký một policy sẽ hướng dẫn Laravel, mà policy để sử dụng khi cho phép hành động cho bởi một model:

<?php

namespace App\Providers;

use App\Post;
use App\Policies\PostPolicy;
use Illuminate\Support\Facades\Gate;
use Illuminate\Foundation\Support\Providers\AuthServiceProvider as ServiceProvider;

class AuthServiceProvider extends ServiceProvider
{
    /**
     * The policy mappings for the application.
     *
     * @var  array
     */
    protected $policies = [
        Post::class => PostPolicy::class,
    ];

    /**
     * Register any application authentication / authorization services.
     *
     * @return  void
     */
    public function boot()
    {
        $this->registerPolicies();

        //
    }
}

Viết Policies

Các phương thức policy

Khi policy đã được đăng ký, bạn có thể thêm hành động ủy quyền cho nó. Ví dụ, xem một phương thức update trong class PostPolicy để định nghĩa User có thể cập nhật Post instance.

Phương thức update sẽ nhận một User và một Post instance là tham số, và trả về true hoặc false khi người dùng có quyền cập nhật Post. Vì vậy, ví dụ này, hãy xác minh id của người dùng khớp với user_id trong bài viết:

<?php

namespace App\Policies;

use App\User;
use App\Post;

class PostPolicy
{
    /**
     * Determine if the given post can be updated by the user.
     *
     * @param    \App\User  $user
     * @param    \App\Post  $post
     * @return  bool
     */
    public function update(User $user, Post $post)
    {
        return $user->id === $post->user_id;
    }
}

Nếu bạn tiếp tục dịnh nghĩa thêm phương thức trong policy khi cần thiết cho các hành động ủy quyền khác của nó. Ví dụ, bạn có thể định nghĩa viewdelete để quỷ quyền hành động Post, nhưng nhớ rằng bạn hoàn toàn thoải mái trong việc đặt tên các phương thức.

Nếu bạn sử dụng lựa chọn --model khi tạo policy qua Artisan console, nó sẽ có sẵn một số phương thức view, create, update, và delete.

Phương thức không có models

Một số phương thức policy chỉ nhận chứng thực user hiện tại và không cần một thể hiện model họ ủy quyền. Trường hợp này rất phổ biến để ủy quyền hành động create. Ví dụ, nếu bạn tạo mới một blog, bạn có thể kiểm tra nếu người dùng là chứng thực thì có thể tạo bất kỳ bài viết.

Khi định nghĩa phương thưc policy sẽ không nhận về thể hiện model, như là một phương thức create, nó sẽ không nhận về một thể hiện model. Vì vậy, bạn có thể định nghĩa phương thứ như là để chức thực người dùng:

/**
 * Determine if the given user can create posts.
 *
 * @param    \App\User  $user
 * @return  bool
 */
public function create(User $user)
{
    //
}

Nếu bạn sử dụng lựa chọn --model khi tạo policy, tất cả phương thức relevant "CRUD" policy sẽ được thêm vào policy.

Lọc policy

Đối với người dùng nhất định, bạn có thể ủy quyền tất cả các hành động trong policy. Đề làm điều này, định nghĩa một phương thức before trong policy. Phương thức before sẽ được thực thi trước bất kỳ phương thức nào trong policy, đem lại cho bạn một cơ hội để cho phép các hành động trước khi các phương thức policy được gọi. Tính năng này thương được sử dụng cho người quản trị có quyền thực hiện tất cả các hành động:

public function before($user, $ability)
{
    if ($user->isSuperAdmin()) {
        return true;
    }
}

Ủy quyền hành động sử dụng policies

Qua User Model

Model User có sẵn trong ứng dụng Laravel đã được thêm vào hai phương thức rất hữu ích cho việc ủy quyền hành động: cancant. Phương thức can nhận một hành động bạn muốn ủy quyền và liên quan đến model. Ví dụ, định nghĩa một người dùng có quyền cập nhật Post:

if ($user->can('update', $post)) {
    //
}

Nếu một policy đã đăng ký cho model, phương thức can sẽ tự động gọi hành động thích hợpvà trả về kiểu boolean. Nếu không policy được đăng ký cho model, phương thức can sẽ cố gắng gọi Closure based Gate khớp với tên hành động.

Hành động không yêu cầu Models

Nhớ rằng, một số hành động như create có thể không cần thể hiện model. Trong trường hợp này, bạn có thể truyền tên class vào phương thức can. Tên class sẽ được sử dụng để xác định cái mà policy để sử dụng ủy quyền cho hành động:

use App\Post;

if ($user->can('create', Post::class)) {
    // Executes the "create" method on the relevant policy...
}

Qua Middleware

Laravel thêm một middleware có thể ủy quyền hành động trước khi có request gửi đến ngay cả đến với routes hoặc controllers. Mặc định, middleware Illuminate\Auth\Middleware\Authorize middleware được gán key can trong class App\Http\Kernel. Hãy khám phá ví dụ can middleware ủy quyền một người dùng có quyền cập nhật bài viết:

use App\Post;

Route::put('/post/{post}', function (Post $post) {
    // The current user may update the post...
})->middleware('can:update,post');

Trong ví dụ trên, chúng ta truyền vào can middleware hai tham số. Thứ nhất là tên của hành động chúng tao ủy quyền và thứ hai là tham số route chúng ta muốn truyền vào phương thức policy. Trong trường hợp này, Khi chúng ta sử dụng implicit model binding, model Post sẽ truyền vào phương thức policy. Nếu người dùng chưa được ủy quyền cho hành động, một HTTP response với một mã 403 status sẽ được sinh ra bởi middleware.

Hành động không yêu cầu Models

Một lần nữa, một số hành động như create có thể không yêu cầu model instance. Trong trường hợp này, bạn có thể truyển tên class vào middleware. Tên class sẽ được sử dụng để xác định cái mà policy sử dụng khi ủy quyền hành động:

Route::post('/post', function () {
    // The current user may create posts...
})->middleware('can:create,App\Post');

Qua Controller Helpers

Ngoài phương thức hữu ích cung câp cho model User, Laravel cung cấp phương thức authorize cho bất cứ controllers kế thừa từ class base App\Http\Controllers\Controller. Như phương thức can, phương thức này chấp nhận tên của hành động bạn muốn ủy quyền và liên quan đến model. Nếu hành động chưa được ủy quyền, hàm authorize sẽ ném ra một Illuminate\Auth\Access\AuthorizationException, mặc định Laravel exception sẽ sử lý và chuyển thành HTTP response với một mã 403 status:

<?php

namespace App\Http\Controllers;

use App\Post;
use Illuminate\Http\Request;
use App\Http\Controllers\Controller;

class PostController extends Controller
{
    /**
     * Update the given blog post.
     *
     * @param    Request  $request
     * @param    Post  $post
     * @return  Response
     */
    public function update(Request $request, Post $post)
    {
        $this->authorize('update', $post);

        // The current user can update the blog post...
    }
}

Hành động không yêu cầu models

Như đã nói ở trước, một số hành động như create không yêu cầu một thể hiện model. Trong trường hợp này, bạn truyền tên class vào phương thức authorize. Tên class sẽ sử dụng để định nghĩa cái mà policy sử dụng để ủy quyền hành động:

/**
 * Create a new blog post.
 *
 * @param    Request  $request
 * @return  Response
 */
public function create(Request $request)
{
    $this->authorize('create', Post::class);

    // The current user can create blog posts...
}

Qua Blade Templates

Khi viết Blade templates, bạn có thể muốn chỉ hiển thị những hành động mà người dùng có quyền. Ví dụ, bạn có thể hiện thị hành động cập nhật form cho những người dùng có quyền cập nhật bài viết. Trong trường hợp này, bạn sử dụng @can and @cannot directives.

@can('update', $post)
    <!-- The Current User Can Update The Post -->
@endcan

@cannot('update', $post)
    <!-- The Current User Can't Update The Post -->
@endcannot

Những directives là viết tắt của câu lệnh @if and @unless. Cậu lệnh @can@cannot tương ứng với các câu lệnh sau:

@if (Auth::user()->can('update', $post))
    <!-- The Current User Can Update The Post -->
@endif

@unless (Auth::user()->can('update', $post))
    <!-- The Current User Can't Update The Post -->
@endunless

Hành động không yêu cầu models

Giống như cách ủy quyền hành động khác, bạn có thể truyền tên class vào @can@cannot directives nếu hành động không yêu cầu thể hiện model:

@can('create', Post::class)
    <!-- The Current User Can Create Posts -->
@endcan

@cannot('create', Post::class)
    <!-- The Current User Can't Create Posts -->
@endcannot
Nguồn: https://laravel.com/docs/5.3/authorization