先做些准备工作, 先建立一个 posts
的migration文件,并成功该表到数据库, 内容如下:
public function up() { Schema::create('posts', function (Blueprint $table) { $table->increments('id'); $table->integer('user_id')->unsigned()->index(); $table->string('title'); $table->text('body'); $table->timestamps(); }); }
到 ModelFactory.php
写上我们需要的测试数据:
$factory->define(App\Post::class, function (Faker\Generator $faker) { return [ 'user_id' => factory(App\User::class)->create()->id, 'title' => $faker->sentence, 'body' => $faker->paragraph ]; });
打开 php artisan tinker
, 生成一个 posts
表的测试数据
Psy Shell v0.8.0 (PHP 7.0.12 — cli) by Justin Hileman >>> factory(App\Post::class)->create(); => App\Post {#695 user_id: 1, title: "Qui adipisci laboriosam qui mollitia quia.", body: "Iusto omnis libero fuga quibusdam. Cum id id libero et necessitatibus ea. Officiis laboriosam occaecati aut.", updated_at: "2016-12-24 06:01:31", created_at: "2016-12-24 06:01:31", id: 1, } >>> App\User::first(); => App\User {#702 id: "1", name: "Ms. Emelia Dooley", email: "erica50@example.org", created_at: "2016-12-24 06:01:31", updated_at: "2016-12-24 06:01:31", } >>>
我们生成了 posts
的一条记录,同时也生成了对应的 User
。
我们再去生成一个 PostsController.php
的控制器
php artisan make:controller PostsController
写个资源路由:
Route::resource('posts', 'PostsController');
我们在控制器中写一个显示某个具体帖子的方法 show($id)
class PostsController extends Controller { public function show($id) { $post = Post::findOrFail($id); return $post->title; } }
浏览器访问: http://localhost:8000/posts/1
到这里都是没有问题的,以前做过很多遍了。
比如我们想做这样一个功能,我们的帖子都是私密帖子,只能作者自己能访问,别人都不能访问,那我们怎么处理,我们可以在控制器中这么写:
class PostsController extends Controller { public function show($id) { $post = Post::findOrFail($id); // 当前登录用户的ID与帖子所属用户的ID不相等 if (auth()->id() != $post->user_id) { // 403 权限错误 abort(403, 'Sorry, not sorrry.'); } return $post->title; } }
上面这样的写法是能够达到我们需要的效果的,可以通过 auth()->LoginUsingId(1)
之类的手动登录用户去测试下。 但是如果在正式项目中,我们肯定不能就这样写在控制器中,如果你这么做了,你的同事势必会抓狂,类似这样的权限控制功能,会经常的在各处使用到,将它封装起来是非常有必要的。
针对这个问题,laravel给了我们提供了 Gates
和 Policies
两种方案,我们先来看第一种 Gates
怎么用(以后我们可以看下laravel是怎么实现这个功能的)
要使用 Gate
, 那我们首先需要去定义(注册)一个 Gate
,涉及到与注册相关的概念,我们肯定会想到服务提供者 Provider
, 到 app/Providers/AuthServiceProvider.php
中,将我们需要的 Gate
定义在 boot()
方法中,为什么要放这里,前面的文章已经讲解的非常清楚了。
public function boot() { $this->registerPolicies(); // 这里的$user是当前登录用户,laravel会处理 // 在调用的时候不用传入 Gate::define('show-post', function ($user, $post)) { return $user->id == $post->user_id; } }
上面代码优化下,方便复用
public function boot() { $this->registerPolicies(); // 这里的$user是当前登录用户,laravel会处理 // 在调用的时候不用传入 Gate::define('show-post', function ($user, $post)) { return $user->owns($post); } } /** 在User.php中 */ public function owns($related) { return $this->id == $related->user_id; }
定义好后,就能按下面这样调用了
public function show($id) { auth()->loginUsingId(1); $post = Post::findOrFail($id); if (Gate::denies('show-post', $post)) { abort(403, 'Sorry, not sorrry.'); } return $post->title; }
或者使用
if (Gate::allows('show-post', $post)) { return $post->title; }
另外控制器的父类使用了一个 AuthorizesRequests
的trait, 在这个trait中有这么一个方法:
public function authorize($ability, $arguments = []) { list($ability, $arguments) = $this->parseAbilityAndArguments($ability, $arguments); return app(Gate::class)->authorize($ability, $arguments); }
所以在控制器中,我们也可以直接这么用:
public function show($id) { auth()->loginUsingId(1); $post = Post::findOrFail($id); // 权限不通过,会抛出Http异常 $this->authorize('show-post', $post); return $post->title; }
我们还可以把权限控制在视图中进行调用,控制器的代码改下:
public function show($id) { auth()->loginUsingId(1); $post = Post::findOrFail($id); return view('posts.show', compact('post')); }
posts/show.blade.php 中
<body> @cannot('show-post', $post) <h1>Sorry, not sorry.</h1> @endcannot @can('show-post', $post) <h1>{{ $post->title }}</h1> @endcan </body>
上面的代码也可以这么写:
<body> @unless (Auth::user()->can('show-post', $post)) Sorry, not sorry. @endunless @if (Auth::user()->can('show-post', $post)) {{ $post->title }} @endif </body>
因为在 User
模型中,laravel为我们提供了 can()和cannot
的方法,同理在控制器中,我们还可以这么写:
if (Auth::user()->cannot('show-post', $post)) { abort(403, 'Sorry, not sorry'); }
最后顺便理下获取当前登录用户的方法:
Auth::user(); // 通过Auth门面 auth()->user(); // 通过auth()帮助函数 $request->user(); // 从$request对象中获得
本节都这里结束.