User authorization


##User Authorization

  • Introduction
  • Gates
    • Write Gates
    • Authorization Action
    • ##Gate Interception check
  • Create policy
    • Generate policy
    • Register policy
  • Writing strategy
    • Strategy method
    • Does not contain model method
    • Guest - User
    • Policy Filter
  • Use Policy Deauthorize action
    • Through user model
    • Through middleware
    • Through controller helper function
    • Via Blade Template

# #Introduction
In addition to providing out-of-the-box user authentication services, Laravel also provides a simple method to handle user authorization actions. Like user authentication, Laravel's authorization method is simple. There are two main ways to operate authorization: gates and policies.

Gates and strategies can be compared to routing and controllers. Gates provide a simple closure-based authorization method, while policies are similar to controllers and implement authorization authentication by grouping their logic around a specific model or resource. We explore gates first and then look at strategies.

When building an application, there is no need to choose between using gates exclusively or using only strategies. Most applications will probably contain both gates and policies, and they will work fine. Gates are mostly used in places where models and resources are not related, such as viewing the administrator's panel. In contrast, strategies should be used within a specific model or resource.

Gates

##

Writing Gates

Gates is a closure function used to determine whether the user is authorized to perform a given action, and the typical method is to use it in
App\Providers\AuthServiceProvider Gate to define. Gates always receives a user instance as the first parameter, and can receive optional parameters, such as the related Eloquent model:

/**
 * 注册任意用户认证、用户授权服务。
 *
 * @return void
 */
 public function boot(){ 
    $this->registerPolicies();    
    Gate::define('update-post', function ($user, $post) {  
          return $user->id == $post->user_id;  
       });
  }

Gates can also use similar controllers Method Class@method style callback string to define:

/**
 * 注册任意用户认证、用户授权服务。
 *
 * @return void
 */
 public function boot(){ 
    $this->registerPolicies();    
    Gate::define('update-post', 'App\Policies\PostPolicy@update');
   }

ResourceGates

You can also use resource Method to define multiple Gate methods at once:

Gate::resource('posts', 'App\Policies\PostPolicy');

The above manual definition has the same effect as the following Gate definition:

Gate::define('posts.view', 'App\Policies\PostPolicy@view');
Gate::define('posts.create', 'App\Policies\PostPolicy@create');
Gate::define('posts.update', 'App\Policies\PostPolicy@update');
Gate::define('posts.delete', 'App\Policies\PostPolicy@delete');

By default, view# will be defined ##, create, update, and delete methods. By passing an array as the third parameter to the resource method. You can override or add to the default method. The keys of the array define the names of the capabilities, and the values ​​define the names of the methods. For example, the following code will create two new Gate definitions - posts.image and posts.photo:

Gate::resource('posts', 'PostPolicy', [ 
   'image' => 'updateImage',    
   'photo' => 'updatePhoto',
 ]);

Authorized actions

When using gates to authorize actions, you should use the

allows or denies method. Note that the current authenticated user does not need to be passed to these methods. Laravel will automatically handle the authenticated user and then pass it to the gete closure function:

if (Gate::allows('update-post', $post)) { 
   // 指定当前用户可以进行更新...
  }
if (Gate::denies('update-post', $post)) { 
   // 指定当前用户不能更新...
  }

If you want to determine whether a specific user has been authorized to access an action, you can use

GateforUser method in facade:

if (Gate::forUser($user)->allows('update-post', $post)) {
    // 用户可以更新...
  }
if (Gate::forUser($user)->denies('update-post', $post)) {
    // 用户不能更新...
  }

Gate interception check

Sometimes, you may want to grant all capabilities to a specific user. So you can use the

before method to define a callback that runs before all other authorization checks:

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

If the

before callback method returns a non-null result, the result will be considered as inspection results.

You can use the

after method to define a callback to be executed after each authorization check. However, you cannot modify the result of the authorization check from the after callback method:

Gate::after(function ($user, $ability, $result, $arguments) { 
   //
});

is similar to the

before check, if the after callback returns non null result, the result will be treated as a check result.

Create policy

Generate Policy

A policy is a class that organizes authorization logic in a specific model or resource. For example, if your application is a blog, then when you create or update the blog, you may have a Post model and a corresponding PostPolicy to authorize user actions.

You can use the make:policy artisan command command in the artisan command artisan command to generate the policy. The generated policies will be placed in the app/Policies directory. If this directory does not exist in your application, Laravel will automatically generate it for you:

php artisan make:policy PostPolicy

make:policy The command will generate an empty policy class. If the class you want to generate contains basic "CRUD" strategy methods, you can specify the --model option when executing the command:

php artisan make:policy PostPolicy --model=Post

{tip} all Strategies are parsed through Laravel's service container, allowing you to type-hint any required dependencies in the policy constructor and have them injected automatically.

Registering Policy

Once a policy exists, it needs to be registered. The AuthServiceProvider included in new Laravel applications has a policies property that maps various models to their policies. Registering a policy will instruct Laravel which policy to use when authorizing actions to access a specified 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{  
      /**
     * 应用的策略映射。
     *
     * @var array
     */ 
     protected $policies = [  
          Post::class => PostPolicy::class,  
       ];   
     /**
     * 注册任意应用认证、应用授权服务
     *
     * @return void
     */ 
   public function boot()   
    {     
       $this->registerPolicies();   
            //   
     }
   }

Policy auto-discovery

As long as the model and policy follow standard Laravel naming By convention, Laravel can automatically discover strategies instead of manually registering model strategies. Specifically, the policy must be located in the Policies directory under the directory containing the model. So, for example, models can be placed in the app directory, and policies can be placed in the app/Policies directory. Additionally, the policy name must match the model name and have the Policy suffix. Therefore, the User model will correspond to the UserPolicy class.

If you want to provide your own policy discovery logic, you can register a custom callback using the Gate::guessPolicyNamesUsing method. Typically, this method should be called from the boot method of your application's AuthServiceProvider:

use Illuminate\Support\Facades\Gate;
Gate::guessPolicyNamesUsing(function ($modelClass) {
    // return policy class name...
 });

{note} Explicit in AuthServiceProvider Any policy that is mapped will take precedence over the auto-discovery policy.

##Writing Strategy

##

Strategy Methods

Once the authorization policy is registered, you can add methods for each action after authorization. For example, we define an update method in PostPolicy, which will determine whether the specified User can update the specified Post instance. The

update method receives User and Post instances as parameters and should return true or false To indicate whether the user is authorized to update the specified Post. So in this example, we need to determine whether the user's id matches the user_id in the post.

<?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;   
     }
 }

You can continue to define additional methods for this authorization policy. For example, you can define the view or delete method to authorize multiple behaviors of Post, and you can also give the custom strategy method a name you like. .

{tip} If you use the --model option when generating a strategy in the Artisan console, it will be included view, create, update and delete action methods.

Does not contain model methods

Some policy methods only receive currently authenticated users as parameters, Instead of passing in the authorization-related model instance. The most common application scenario is to authorize the create action. For example, if you are creating a blog, you may want to first check whether the current user has permission to create it.

When defining a strategy method that does not require passing in a model instance, such as the create method, it does not receive a model instance as a parameter. You should define this method to only accept authorized users as parameters.

/**
 * 判断用户是否可以创建请求。
 *
 * @param  \App\User  $user
 * @return bool
 */
 public function create(User $user){ 
    //
 }

Guest User

By default, if the incoming HTTP request is not initiated by an authenticated user , then all gates and strategies will automatically return false. However, you can allow these authorization checks to be passed into your gates and policies by declaring an "optional" type hint or providing a null default value for the user parameter definition:

<?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;  
     }
  }

Policy Filter

For a specific user, you may want to authorize all actions through a specified policy. To achieve this goal, you can define a before method in the strategy. The before method will be executed before all other methods in the policy, thus providing a way to authorize actions other than the specified policy method to perform judgment. The most common scenario for this feature is to authorize the application's administrator to have access to all actions:

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

If you want to deny all authorizations to a user, you should return ## in the before method #false . If the return value is null, then authorization will fail in this policy.

{note} The

before method of a strategy class will not be called if the class does not contain a method matching the function name being checked.

##Use policy authorization actions

Through the User Model

Laravel’s built-in

User

model contains two useful methods for authorizing actions: can and cant . This can method needs to specify the authorized action and related model. For example, to determine whether a user is authorized to update the specified Post model:

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

policy of the specified model has been registered

", can The method automatically calls the appropriate strategy and returns a boolean value. If no strategy is registered to this model, the can method will attempt to call a closure-based Gate that matches the specified action name.

No need to specify model actions

Remember that some actions, such as

create

, do not require specifying a model instance. In this case, pass a class name to the can method. This class name will be used to determine which policy authorization action to use:

use App\Post;if ($user->can('create', Post::class)) { 
   // 执行相关策略中的 "create" 方法...
 }

Via middleware

Laravel includes A middleware that can authorize actions before the request reaches the route or controller. By default, the

Illuminate\Auth\Middleware\Authorize

middleware is assigned to the can key in your App\Http\Kernel class. Let us use an example of an authorized user updating a blog to explain the use of the can middleware:

use App\Post;Route::put('/post/{post}', function (Post $post) {
    // 当前用户可以进行更新操作...
})->middleware('can:update,post');
In this example, we passed the

can

middleware two parameters. The first parameter is the name of the action that requires authorization, and the second parameter is the routing parameter we want to pass to the policy method. In this case, we use "implicit route binding" and a Post model is passed to the strategy method. If the user is not authorized to access the specified action, this middleware will generate an HTTP response with the 403 status code.

Actions that do not require a specified model

Similarly, some actions like create may not require a model instance. In this case, you can pass a class name to the middleware. When authorizing this action, this class name will be used to determine which policy to use:

Route::post('/post', function () {
    // 当前用户可以进行创建操作...
 })->middleware('can:create,App\Post');

Through the controller helper function

In addition to providing helper methods in the User model, Laravel also provides a useful for controllers that inherit the App\Http\Controllers\Controller base class. authorize method. Just like the can method, this method needs to receive the action you want to authorize and the related model as parameters. If this action is not authorized, the authorize method will throw an exception of Illuminate\Auth\Access\AuthorizationException, and then Laravel's default exception handler will convert this exception into an exception with 403 HTTP response status code.

<?php
     namespace App\Http\Controllers;
     use App\Post;use Illuminate\Http\Request;
     use App\Http\Controllers\Controller;
     class PostController extends Controller{  
       /**
     * 更新指定博客帖子。
     *
     * @param  Request  $request
     * @param  Post  $post
     * @return Response
     * @throws \Illuminate\Auth\Access\AuthorizationException
     */ 
   public function update(Request $request, Post $post)  
     {     
       $this->authorize('update', $post);     
       // 当前用户可以更新博客...   
      }
  }

No need to specify the action of the model

As discussed before, some actions, such as create, do not need to specify the action of the model instance . In this case, you can pass a class name to the authorize method. When authorizing this action, this class name will be used to determine which policy to use:

/**
 * 创建一个新的博客
 *
 * @param  Request  $request
 * @return Response
 * @throws \Illuminate\Auth\Access\AuthorizationException
 */
 public function create(Request $request){ 
    $this->authorize('create', Post::class);  
    // 当前用户可以新建博客...
  }

Authorization Resource Controller

If you are using a resource controller, then You can use the authorizeResource method in the controller constructor. This method will attach the appropriate can middleware to the corresponding method of the resource controller.

authorizeResource The method receives the template class name as the first parameter and the name of the route/request parameter containing the model ID as its second parameter:

<?php
    namespace App\Http\Controllers;
    use App\Post;use Illuminate\Http\Request;
    use App\Http\Controllers\Controller;
    class PostController extends Controller{ 
       public function __construct() 
          {       
            $this->authorizeResource(Post::class, 'post');  
           }
     }

{Tip} You can use the make:policy command with the --model option to quickly generate a policy class based on a given model: php artisan make:policy PostPolicy --model=Post.

##Through Blade Templates

When writing Blade templates, you may want only specific portions of the page to Displayed to users authorized to access the specified action. For example, you might want to display updated forms only to users who have permission to update their blog. In this case, you can use a series of instructions such as

@can and @cannot:

@can('update', $post) 
  <!-- The Current User Can Update The Post -->
@elsecan('create', App\Post::class)  
  <!-- The Current User Can Create New Post -->
@endcan
@cannot('update', $post) 
  <!-- The Current User Can't Update The Post -->
@elsecannot('create', App\Post::class)
  <!-- The Current User Can't Create New Post -->
 @endcannot

These instructions are written

@if and @unless Shortcut to statement. The @can and @cannot statements are converted into the following statements respectively:

@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

Model-independent actions

As with most other authorization methods, if the action does not require a model instance, you can pass the class name to @ can and @ cannot Instructions:

@can('create', App\Post::class) 
   <!-- The Current User Can Create Posts -->
@endcan
@cannot('create', App\Post::class)   
      <!-- The Current User Can't Create Posts -->
@endcannot
This article was first published on the LearnKu.com website.