[文档:https://laravel-china.org/docs/laravel/5.5/authorization/1310](https://laravel-china.org/docs/laravel/5.5/authorization/1310)
## 创建策略:
策略是在特定模型或者资源中组织授权逻辑的类。
例如:给用户的操作创建一个策略里
```
php artisan make:policy UserPolicy
```
该命令会创建一个文件:app/Policies/UserPolicy.php
make:policy 会生成空的策略类。
==如果希望生成的类包含基本的「CRUD」策略方法, 可以在使用命令时指定 --model 选项:==
```
php artisan make:policy UserPolicy --model=User
```
如下:
```
<?php
namespace App\Policies;
use App\User;
use Illuminate\Auth\Access\HandlesAuthorization;
class UserPolicy
{
use HandlesAuthorization;
/**
* Determine whether the user can view the model.
*
* @param \App\User $user
* @param \App\User $model
* @return mixed
*/
public function view(User $user, User $model)
{
//
}
/**
* Determine whether the user can create models.
*
* @param \App\User $user
* @return mixed
*/
public function create(User $user)
{
//
}
/**
* Determine whether the user can update the model.
*
* @param \App\User $user
* @param \App\User $model
* @return mixed
*/
public function update(User $user, User $model)
{
//
}
/**
* Determine whether the user can delete the model.
*
* @param \App\User $user
* @param \App\User $model
* @return mixed
*/
public function delete(User $user, User $model)
{
//
}
}
```
## 注册策略
一旦该授权策略存在,需要将它进行注册。新的 Laravel 应用中包含的 AuthServiceProvider 包含了一个 policies 属性,可将各种模型对应至管理它们的授权策略。注册一个策略将引导 Laravel 在授权动作访问指定模型时使用何种策略:
文件:app/Providers/AuthServiceProvider.php
```
<?php
namespace App\Providers;
use Illuminate\Foundation\Support\Providers\AuthServiceProvider as ServiceProvider;
class AuthServiceProvider extends ServiceProvider
{
/**
* The policy mappings for the application.
*
* @var array
*/
protected $policies = [
'App\Model' => 'App\Policies\ModelPolicy',
'App\User' => 'App\Policies\UserPolicy',//注册策略类
Post::class => PostPolicy::class,
];
/**
* Register any authentication / authorization services.
*
* @return void
*/
public function boot()
{
$this->registerPolicies();
//
}
}
```
## 编写策略
### 策略方法
一旦授权策略被生成且注册,我们就可以为授权的每个动作添加方法。
```
<?php
namespace App\Policies;
use App\User;
use Illuminate\Auth\Access\HandlesAuthorization;
class UserPolicy
{
use HandlesAuthorization;
/**
* Determine whether the user can view the model.
* 判断用户列表能否被用户查询
* @param \App\User $user 当前登录的用户
* @param \App\User $model 操作的对象
* @return mixed
*/
public function view(User $user, User $model)
{
//
return $user->can('manage_users');
}
/**
* Determine whether the user can create models.
* 判断是否有权限创建用户
* @param \App\User $user 当前登录用户
* @return mixed
*/
public function create(User $user)
{
//
return $user->can('manage_users');
}
/**
* Determine whether the user can update the model.
* 判断是否有权限更新用户:要求拥有用户管理权限或者是用户自己
* @param \App\User $user
* @param \App\User $model
* @return mixed
*/
public function update(User $user, User $model)
{
//
return $user->can('manage_users') || $user->id == $model->id;
}
/**
* Determine whether the user can delete the model.
* 判断是否有权限删除用户
* @param \App\User $user
* @param \App\User $model
* @return mixed
*/
public function delete(User $user, User $model)
{
//
return $user->can('manage_users');
}
}
```
## 策略过滤器
对特定用户,你可能希望通过指定的策略授权所有动作。 要达到这个目的,可以在策略中定义一个 before 方法。before 方法会在策略中其它所有方法之前执行,这样提供了一种方式来授权动作而不是指定的策略方法来执行判断。这个功能最常见的场景是授权应用的管理员可以访问所有动作:
创建如下策略类:Policy,并让其他策略类继承此类。
```
<?php
namespace App\Policies;
use App\User;
use Illuminate\Auth\Access\HandlesAuthorization;
class Policy
{
use HandlesAuthorization;
/**
* Create a new policy instance.
*
* @return void
*/
public function __construct()
{
//
}
public function before(User $user, $ability)
{
if ($user->isSuperAdmin()) {
return true;
}
}
}
```
## 使用策略授权动作
### 通过用户模型
Laravel 应用内置的 User 模型包含 2 个有用的方法来授权动作:can 和 cant。can 方法需要指定授权的动作和相关的模型。
如下:ajax请求 权限检验代码
```
/**
* 添加
* @param UserRequest $request
* @param Result $result
* @return array
*/
public function store(UserRequest $request, Result $result)
{
if ($request->user()->cant('create', User::class)) {
return $result->failed('没有权限')->toArray();
}
try {
$data = $request->only(['name', 'username', 'email', 'password', 'status', 'sex', 'bool_admin']);
$data['password'] = bcrypt($data['password']);
$user = User::create($data);
$roles = $request->input('roles') ? $request->input('roles') : [];
$user->assignRole($roles);
// 要求:User和配置的 auth.default.guard 对应的provider 使用的 user model 为同一个类
//(因为 Role 默认的guard为配置)
$result->succeed($user);
} catch (\Exception $exception) {
$result->failed($exception->getMessage());
}
return $result->toArray();
}
/**
* 更新
*
* @param UserRequest $request
* @param $id
* @param Result $result
* @return array
*/
public function update(UserRequest $request, $id, Result $result)
{
$user = User::find($id);
if ($request->user()->cant('update', $user)) {
return $result->failed('没有权限')->toArray();
}
$data = $request->only(['name', 'username', 'email', 'password', 'status', 'sex', 'bool_admin']);
$user->update($data);
$roles = $request->input('roles') ? $request->input('roles') : [];
$user->syncRoles($roles);
$result->succeed($user);
return $result->toArray();
}
```
### 视图权限控制
如下:创建用户的页面权限-若无权限抛出异常 AuthorizationException
```
/**
* 新增页面
* @param User $user
* @return \Illuminate\Contracts\View\Factory|\Illuminate\View\View
* @throws \Illuminate\Auth\Access\AuthorizationException
*/
public function create(User $user)
{
$this->authorize('create', User::class);
$roles = Role::get()->pluck('name', 'remarks')->toArray();
$userRoles = [];
return $this->backend_view('users.create_edit', compact('user', 'roles', 'userRoles'));
}
```
处理抛出的授权异常:在app/Exceptions/Handler.php中判断异常类型,若是授权异常,则跳转到无权限页面
```
/**
* Render an exception into an HTTP response.
* @param \Illuminate\Http\Request $request
* @param Exception $exception
* @return \Illuminate\Http\RedirectResponse|\Symfony\Component\HttpFoundation\Response
*/
public function render($request, Exception $exception)
{
if ($exception instanceof AuthorizationException) {
return redirect()->guest(route('admin.permission-denied'));
}
return parent::render($request, $exception);
}
```
## 通过控制器辅助函数
除了在 User 模型中提供辅助方法外,Laravel 也为所有继承了 App\Http\Controllers\Controller 基类的控制器提供了一个有用的 authorize 方法。和 can 方法类似,这个方法接收需要授权的动作和相关的模型作为参数。如果动作不被授权,authorize 方法会抛出 Illuminate\Auth\Access\AuthorizationException 异常,然后被 Laravel 默认的异常处理器转化为带有 403 状态码的 HTTP 响应:
```
<?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
*/
public function update(Request $request, Post $post)
{
$this->authorize('update', $post);
// 当前用户可以更新博客...
}
}
```
遇见异常:
```
protected function prepareException(Exception $e)
{
if ($e instanceof ModelNotFoundException) {
$e = new NotFoundHttpException($e->getMessage(), $e);
} elseif ($e instanceof AuthorizationException) {
$e = new AccessDeniedHttpException($e->getMessage(), $e);
} elseif ($e instanceof TokenMismatchException) {
$e = new HttpException(419, $e->getMessage(), $e);
}
return $e;
}
//"This action is unauthorized."
```
原因:未注册用户的策略类UserPolicy 到 app/Providers/AuthServiceProvider.php (注意:每个策略类都要和对应的model绑定)
解决方案:
```
<?php
namespace App\Providers;
use Illuminate\Foundation\Support\Providers\AuthServiceProvider as ServiceProvider;
class AuthServiceProvider extends ServiceProvider
{
/**
* The policy mappings for the application.
*
* @var array
*/
protected $policies = [
'App\Model' => 'App\Policies\ModelPolicy',
'App\User' => 'App\Policies\UserPolicy',//注册策略类
];
/**
* Register any authentication / authorization services.
*
* @return void
*/
public function boot()
{
$this->registerPolicies();
//
}
}
```