先做些准备工作, 先建立一个 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: "[email protected]",
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对象中获得
本节都这里结束.