Project-Flyer-14-Guards-and-Authorization

跟随 Jeffrey Way 的视频学习,脑子不好,一些细节容易忘记,这里记录下来。相关视频在 laracasts.com
课程名称:Build “ProjectFlyer” With Me(14)

现在我们让顶部导航能显示登录用户的信息等。

打开 layouts/master.blade.php,在

</div><!-- /.navbar-collapse -->

前面添加:

@if(Auth::check())
<p class="navbar-text navbar-right">
Hello, {{ Auth::user()->name }}
</p>
@endif

测试通过/auth/register来注册,成功注册后应该会在导航栏显示登录用户名。
好,把刚添加的那段代码修改为:

@if($signedIn)
<p class="navbar-text navbar-right">
Hello, {{ $user->name }}
</p>
@endif

此时刷新页面会报错,错误位置指向了@if($signedIn),我们现在去控制器添加$signedIn
打开 app\Http\Controllers\Controller.php,添加构造方法:

public function __construct()
{
view()->share('signedIn', Auth::check());
}

添加 Auth 的引用,然后我们在 FlyerController 里调用刚添加的构造函数:

parent::__construct();

刷新页面会发现有新的报错:Undefined variable: user,在刚才的构造函数里添加 user

view()->share('user', Auth::user());

现在刷新页面,一切正常了。我们现在退出登录:/auth/logout,报错了:Undefined variable: signedIn。问题应该出了路由上面了,没有 signedIn 的路由和模板。
打开 router,在第一行添加(原作者是这么干的):

Route::get('/', 'PagesController@home');

注意到,新加的这条路由里的控制器是不存在的,我们现在创建它,进入 Homestead 虚拟机网站目录下,执行:

php artisan make:controller PagesController --plain

现在给 PagesController 里添加:

public function home()
{
return view('pages.home');
}

现在删除 routes.php 里原有的路由:

Route::get('/', function () {
return view('pages.home');
});

刷新下,还是刚才的错误,没错,我们还没修改 AuthController 控制器里的方法。
打开 AuthController 控制器,在其构造方法 __construct 里添加一行父级构造方法的调用:

parent::__construct();

此时调用登录或注册页面都正常了,试下添加新内容和上传图片也都是正常的。

修改 FlyerController 控制器的 addPhoto 方法为:

public function addPhoto($zip, $street, Request $request)
{
$this->validate($request, [
'photo' => 'required|mimes:jpg,jpeg,png,gif,bmp'
]);

$flyer = Flyer::locatedAt($zip, $street);

if ($flyer->user_id !== \Auth::id()) {
if ($request->ajax()) {
return response(['message'=>'No way.'], 403);
}

flash('No way.');

return redirect('/');
}

$photo = $this->makePhoto($request->file('photo'));
$flyer->addPhoto($photo);

// $flyer->photos()->create(['path' => "/uploads/photos/{$name}"]);

return 'Done';
}

注意到 flyer 表里没有 user_id 这个字段,我们添加它,打开数据库迁移文件 *_create_flyer_table.php ,在 up()方法中添加一行:

$table->integer('user_id')->unsigned();

在 Homestead 虚拟机里网站根目录下运行:

php artisan migrate:refresh

查询下数据库可以发现已经成功给 flyers 数据表添加了 user_id 的字段,试着添加一条数据,我们会发现 user_id 的内容是0,这不是我们想要的结果,我们现在修改 ModelFactory.php,修改第二段 Flyer 那段的代码,在street 的上面添加一行:

'user_id'       => factory('App\User')->create()->id,

去数据库删除刚才添加的那条数据,进入 homestead 虚拟机环境网站根目录运行:

php artisan tinker

factory('App\Flyer')->create();

可以看到数据库里在 flyers 和 users 里添加了一行数据,我当时奇怪了下怎么会连用户也自动添加了,原来是 faker 的功劳,可以证明,我们刚才添加到 ModelFactory.php 里的那行代码起作用了。浏览刚添加的数据,在地址栏网站域名后面接/zip/street,zip 就是新添加的那行的 zip,street 也是,我们可以看到刚才添加的数据了。但是我们如果在这个地方上传图片会失败。因为我们没有登录,后面会发现,即使用正确的账号登录也会上传失败,并且会在开发人员工具里发现报错No Way,下面我们来解决这个问题。

先记录几条作者通过代码添加用户和加密用户密码,并保存的命令行:

php artisan tinker
factory('App\User')->create();
$user = App\User::find(2);
$user->password = bcrypt("password");
$user->save();

我们先添加一个中间件 MustOwnFlyer :

php artisan make:middleware MustOwnFlyer

打开app\Flyer.php,添加方法:

/**
* A Flyer is owned by a user
* @return Illuminate\Database\Eloquent\Relations\BelongsTo
*/
public function owner()
{
return $this->belongsTo('App\User', 'user_id');
}
/**
* Determine if the given user created the flyer
* @param User $user
* @return boolen
*/
public function ownerBy(User $user)
{
return $this->user_id == $user->id;
}

打开 FlyerController ,修改:

if ($flyer->user_id !== \Auth::id()) {
这里的代码放到新的方法里
}

为:

if ($flyer->ownerBy($this->user)) {
return $this->unauthorized();
}

添加新方法 unauthorized,内容是上面 if 代码段里的内容,如下:

public function unauthorized(Request $request)
{
if ($request->ajax()) {
return response(['message'=>'No way.'], 403);
}

flash('No way.');

return redirect('/');
}

打开 Controller.php,修改Controller类为:

abstract class Controller extends BaseController
{
use AuthorizesRequests, DispatchesJobs, ValidatesRequests;
protected $user;
public function __construct()
{
$this->user = Auth::user();
view()->share('signedIn', Auth::check());
view()->share('user', $this->user);
}
}

我们试着登录上传图片发现图片无法上传,继续修改 FlyersController 里 addPhoto 代码段为:

public function addPhoto($zip, $street, Request $request)
{
$this->validate($request, [
'photo' => 'required|mimes:jpg,jpeg,png,gif,bmp'
]);

if (! $this->userCreatedFlyer($request)) {
return $this->unauthorized($request);
}

$photo = $this->makePhoto($request->file('photo'));
Flyer::locatedAt($zip, $street)->addPhoto($photo);

return 'Done';
}

添加方法:

public function userCreatedFlyer(Request $request)
{
return Flyer::where([
'zip' => $request->zip,
'street' => $request->street,
'user_id' => $this->user->id
])->exiest();
}

试一下,图片报错信息是500错误,ErrorException in FlyersController.php line 83:
我们把 userCreatedFlyer 方法和 unauthorized 方法抽象到其他文件里,在

class FlyersController extends Controller
{

下面添加:

use AuthorizesUsers;

然后添加新文件:app/Http/Controllers/Traits/AuthorizesUsers.php,把 userCreatedFlyer 和 unauthorized 方法剪切到新建的文件里:

<?php 
namespace App\Http\Controllers\Traits;
trait AuthorizesUsers{
public function userCreatedFlyer(Request $request)
{
return Flyer::where([
'zip' => $request->zip,
'street' => $request->street,
'user_id' => $this->user->id
])->exiest();
}

public function unauthorized(Request $request)
{
if ($request->ajax()) {
return response(['message'=>'No way.'], 403);
}

flash('No way.');

return redirect('/');
}
}

回到 FlyerController 控制器,添加AuthorizesUsers的引用,顶部应用最后为:

use App\Flyer;
use App\Photo;
use Illuminate\Http\Request;
use App\Http\Requests\FlyerRequest;
use App\Http\Controllers\Controller;
use App\Http\Controllers\Traits\AuthorizesUsers;
use Symfony\Component\HttpFoundation\File\UploadedFile;

此时再上传试试,会发现图片的报错信息有一条是:
Argument 1 passed to App\Http\Controllers\FlyersController::userCreatedFlyer() must be an instance of App\Http\Controllers\Traits\Request
我们为 AuthorizesUsers 添加引用:

use App\Flyer;
use Illuminate\Http\Request;

现在我们再测试图片上传,发现可以返回我们设定的错误内容:No Way.
注释 FlyersController 里 addPhoto 方法的下面代码:

$this->validate($request, [
'photo' => 'required|mimes:jpg,jpeg,png,gif,bmp'
]);

if (! $this->userCreatedFlyer($request)) {
return $this->unauthorized($request);
}
```
删除 类里面的
`use AuthorizesUsers;`

`use App\Http\Controllers\Traits\AuthorizesUsers;`
修改 addPhoto 方法句柄为:
public function addPhoto($zip, $street, ChangeFlyerRequest $request)
然后在 Homestead 虚拟机网站根目录下运行:

php artisan make:request ChangeFlyerRequest

修改 ChangeFlyerRequest.php 里的 rules 方法的返回值为:

return [
‘photo’ => ‘required|mimes:jpg,jpeg,png,gif,bmp’
];

修改 authorize 方法的返回值为:

return Flyer::where([
‘zip’ => $this->zip,
‘street’ => $this->street,
‘user_id’ => $this->user()->id
])->exists();

删除刚才在 FlyersController 里注释的代码。最终,addPhoto 方法为:

/**

  • Apply a photo to the referenced flyer

  • @param string $zip

  • @param string $street

  • @param ChangeFlyerRequest $request

  • /
    public function addPhoto($zip, $street, ChangeFlyerRequest $request)
    {
    $photo = $this->makePhoto($request->file(‘photo’));

    Flyer::locatedAt($zip, $street)->addPhoto($photo);
    }

好了,现在在上传图片会报错:Forbidden,重新登录应该可以上传成功了。
我遇到了这样的问题:如果是从网页的页面添加新 flyer 信息,在数据库里的 user_id 的值会是0,这会导致我无论如何都无法上传成功图片的,会返回 Forbidden,如果用命令行工具添加新的 flyer 信息就可以插入 user_id 但是该 id 是和 flyer的 id 是一样的值,这个我没搞明白。

0%