71.邮箱认证(一)

未匹配的标注

本节说明

  • 对应视频教程第 71 小节:Users Must Confirm Their Email Address: #1 - Protection

本节内容

我们继续开发下一个功能:邮箱认证。按照惯例,我们新建测试:
forum\tests\Feature\CreateThreadsTest.php

 . . /** @test */ public function guests_may_not_create_threads() { $this->withExceptionHandling(); $this->get('/threads/create') ->assertRedirect('/login'); $this->post('/threads') ->assertRedirect('/login'); } /** @test */ public function authenticated_users_must_first_confirm_their_email_address_before_creating_threads() { $this->publishThread() ->assertRedirect('/threads') ->assertSessionHas('flash','You must first confirm your email address.'); } . .

接下来我们修改迁移文件,为users表增加一个confirmed字段:
forum\database\migrations\2014_10_12_000000_create_users_table.php

 . . public function up() { Schema::create('users', function (Blueprint $table) { $table->increments('id'); $table->string('name'); $table->string('email')->unique(); $table->string('password'); $table->string('avatar_path')->nullable(); $table->boolean('confirmed')->default(false); $table->rememberToken(); $table->timestamps(); }); } . .

运行迁移:

$ php artisan migrate:refresh

进入Tinker:

$ php artisan tinker

填充数据:

>>> factory('App\Thread',30)->create();

为了方便测试,我们直接在模型工厂文件中给confirmed字段返回false
forum\database\factories\ModelFactory.php

. . $factory->define(App\User::class, function (Faker\Generator $faker) { static $password; return [ 'name' => $faker->name, 'email' => $faker->unique()->safeEmail, 'password' => $password ?: $password = bcrypt('123456'), 'remember_token' => str_random(10), 'confirmed' => false, ]; }); . .

我们接下来修改控制器使测试通过:
forum\app\Http\Controllers\ThreadsController.php

 . . public function store(Request $request) { if (! auth()->user()->confirmed) { return redirect('/threads')->with('flash','You must first confirm your email address.'); } $this->validate($request,[ 'title' => 'required|spamfree', 'body' => 'required|spamfree', 'channel_id' => 'required|exists:channels,id' ]); $thread = Thread::create([ 'user_id' => auth()->id(), 'channel_id' => request('channel_id'), 'title' => request('title'), 'body' => request('body'), ]); return redirect($thread->path()) ->with('flash','Your thread has been published!'); } . .

运行测试:
file

接下来我们将利用 Laravel 中间件 功能来过滤掉未认证用户发表话题的请求。

本站教程内容引用

我们引用本站第二本教程 L02 Laravel 教程 - Web 开发实战进阶 ( Lara... 对中间件的说明:

Laravel 中间件提供了一种方便的机制来过滤进入应用的 HTTP 请求。例如,Laravel 内置了一个中间件来验证用户的身份认证。如果用户没有通过身份认证,中间件会将用户重定向到登录界面。但是,如果用户被认证,中间件将允许该请求进一步进入该应用。

当然,除了身份认证以外,中间件还可以用来执行各种任务。例如:CORS 中间件可以负责为所有离开应用的响应添加合适的头部信息;日志中间件可以记录所有传入应用的请求。Laravel 自带了一些中间件,包括身份验证、CSRF 保护等。所有这些中间件都位于 app/Http/Middleware 目录中。

Laravel 的中间件从执行时机上分『前置中间件』和『后置中间件』,前置中间件是应用初始化完成以后立刻执行,此时控制器路由还未分配、控制器还未执行、视图还未渲染。后置中间件是即将离开应用的响应,此时控制器已将渲染好的视图返回,我们可以在后置中间件里修改响应。两者的区别在于书写方式的不同:

前置中间件:

<?php namespace App\Http\Middleware; use Closure; class BeforeMiddleware { public function handle($request, Closure $next) { // 这是前置中间件,在还未进入 $next 之前调用 return $next($request); } }

后置中间件:

<?php namespace App\Http\Middleware; use Closure; class AfterMiddleware { public function handle($request, Closure $next) { $response = $next($request); // 这是后置中间件,$next 已经执行完毕并返回响应 $response, // 我们可以在此处对响应进行修改。 return $response; } }

注意他们的区别在于 $next($request) 的执行位置,而非类的命名或者其他。

创建中间件

运行以下命令,生成中间件类文件:

$ php artisan make:middleware RedirectIfEmailNotConfirmed

注册中间件

想让中间件在应用的每个 HTTP 请求期间运行,我们还需要在 app/Http/Kernel.php 类中对中间件进行注册。
forum\app\Http\Kernel.php

<?php namespace App\Http; use App\Http\Middleware\RedirectIfEmailNotConfirmed; use Illuminate\Foundation\Http\Kernel as HttpKernel; class Kernel extends HttpKernel { . . protected $routeMiddleware = [ 'auth' => \Illuminate\Auth\Middleware\Authenticate::class, 'auth.basic' => \Illuminate\Auth\Middleware\AuthenticateWithBasicAuth::class, 'bindings' => \Illuminate\Routing\Middleware\SubstituteBindings::class, 'can' => \Illuminate\Auth\Middleware\Authorize::class, 'guest' => \App\Http\Middleware\RedirectIfAuthenticated::class, 'throttle' => \Illuminate\Routing\Middleware\ThrottleRequests::class, 'must-be-confirmed' => \App\Http\Middleware\RedirectIfEmailNotConfirmed::class ]; }

书写中间件类

forum\app\Http\Middleware\RedirectIfEmailNotConfirmed.php

<?php namespace App\Http\Middleware; use Closure; class RedirectIfEmailNotConfirmed { public function handle($request, Closure $next) { if (! $request->user()->confirmed) { // 如果用户未认证,则重定向 return redirect('/threads')->with('flash','You must first confirm your email address.'); } return $next($request); } }

应用中间件

我们删除控制器的代码片段:
forum\app\Http\Controllers\ThreadsController.php

 . . public function store(Request $request) { $this->validate($request,[ 'title' => 'required|spamfree', 'body' => 'required|spamfree', 'channel_id' => 'required|exists:channels,id' ]); $thread = Thread::create([ 'user_id' => auth()->id(), 'channel_id' => request('channel_id'), 'title' => request('title'), 'body' => request('body'), ]); return redirect($thread->path()) ->with('flash','Your thread has been published!'); } . .

应用中间件:
forum\routes\web.php

. . Route::post('threads','ThreadsController@store')->middleware('must-be-confirmed'); . .

再次运行测试:
file
测试通过,但是我们还有一个小问题需要注意下:如果我们现在用一个未认证用户尝试发布话题,的确我们会被重定向至话题列表页面,但是我们存入session的消息没有显示出来。我们期望显示消息,所以我们需要修改Flash组件:
forum\resources\assets\js\components\Flash.vue

<template> <div class="alert alert-flash" :class="'alert-'+level" role="alert" v-show="show" v-text="body"> </div> </template> <script> export default { props:['message'], data(){ return { body : this.message, // 此处赋予初始值 level : 'success', show:false } }, created(){ if(this.message){ this.flash(); // 更改逻辑为:如果有消息,则展示 } window.events.$on( 'flash',data => this.flash(data) ); }, methods:{ flash(data){ // 修改处理方式:如果传入了对象参数,则重写 body,level if(data) { this.body = data.message; this.level = data.level; } this.show = true; this.hide(); }, hide(){ setTimeout( () => { this.show = false; },3000); } } }; </script> <style> .alert-flash{ position: fixed; right: 25px; bottom: 25px; } </style>

现在我们再来尝试:
file

本文章首发在 LearnKu.com 网站上。

上一篇 下一篇
《L04 微信小程序从零到发布》
从小程序个人账户申请开始,带你一步步进行开发一个微信小程序,直到提交微信控制台上线发布。
《L03 构架 API 服务器》
你将学到如 RESTFul 设计风格、PostMan 的使用、OAuth 流程,JWT 概念及使用 和 API 开发相关的进阶知识。
讨论数量: 0
发起讨论 只看当前版本


暂无话题~