1. 前言

上一节我们介绍了 Laravel 用户认证系统基本概念,这一节来更深入的了解如何使用。

2. 路由

使用以下命令来快速生成身份认证所需的路由和视图:

php artisan make:auth

该命令会自动创建用户认证相关的控制器:

  • app/Http/Controllers/Auth/ForgotPasswordController.php - 用于忘记密码后发送找回密码邮件
  • app/Http/Controllers/Auth/LoginController.php - 用于登录
  • app/Http/Controllers/Auth/RegisterController.php - 用于注册
  • app/Http/Controllers/Auth/ResetPasswordController.php - 用于重置密码

同时在路由 routes/web.php 中添加用户认证所需路由:

.
.
.
Auth::routes();

这个方法定义了哪些路由呢?我们一步步来找找。

首先在 config/app.php 中找到 Auth 的 alias:

.
.
.
   'aliases' => [

        'App' => Illuminate\Support\Facades\App::class,
        'Artisan' => Illuminate\Support\Facades\Artisan::class,
        'Auth' => Illuminate\Support\Facades\Auth::class,
.
.
.

找到 Illuminate\Support\Facades\Auth 这个门面,可以看到里面的 routes 方法:

.
.
.
    public static function routes()
    {
        static::$app->make('router')->auth();
    }
.
.
.

这里实例化了一个 Router 类,然后找到对应的 Router 类 Illuminate\Routing\Router

.
.
.
    public function auth()
    {
        // Authentication Routes...
        $this->get('login', 'Auth\LoginController@showLoginForm')->name('login');
        $this->post('login', 'Auth\LoginController@login');
        $this->post('logout', 'Auth\LoginController@logout')->name('logout');

        // Registration Routes...
        $this->get('register', 'Auth\RegisterController@showRegistrationForm')->name('register');
        $this->post('register', 'Auth\RegisterController@register');

        // Password Reset Routes...
        $this->get('password/reset', 'Auth\ForgotPasswordController@showLinkRequestForm')->name('password.request');
        $this->post('password/email', 'Auth\ForgotPasswordController@sendResetLinkEmail')->name('password.email');
        $this->get('password/reset/{token}', 'Auth\ResetPasswordController@showResetForm')->name('password.reset');
        $this->post('password/reset', 'Auth\ResetPasswordController@reset');
    }
.
.
.

到这里一切都水落石出了。

3. 注册成功后通知

如果新用户注册之后,需要给用户发送一个通知,要怎么实现呢?

可以用上一节提到的用监听器来监听指定的事件。

app/Http/Controllers/Auth/RegisterController.php 里引用了 Illuminate\Foundation\Auth\RegistersUsers 这个 trait,注册流程如下:

.
.
.
    public function register(Request $request)
    {
        $this->validator($request->all())->validate();

        event(new Registered($user = $this->create($request->all())));

        $this->guard()->login($user);

        return $this->registered($request, $user)
                        ?: redirect($this->redirectPath());
    }
.
.
.

注册成功之后会触发 Illuminate\Auth\Events\Registered 事件,所以我们只需要监听该事件就行了。

新建一个监听器:

php artisan make:listener RegisteredListener

内容如下:

<?php

namespace App\Listeners;

use App\Notifications\EmailVerificationNotification;
use Illuminate\Auth\Events\Registered;
use Illuminate\Queue\InteractsWithQueue;
use Illuminate\Contracts\Queue\ShouldQueue;

// implements ShouldQueue 让这个监听器异步执行
class RegisteredListener implements ShouldQueue
{
    // 当事件被触发时,对应该事件的监听器的 handle() 方法就会被调用
    public function handle(Registered $event)
    {
        // 获取到刚刚注册的用户
        $user = $event->user;
        // 调用 notify 发送通知
        $user->notify(new EmailVerificationNotification());
    }
}

监听器创建完成之后记得在 EventServiceProvider 中将事件和监听器关联起来才能生效:

app/Providers/EventServiceProvider.php

use App\Listeners\RegisteredListener;
use Illuminate\Auth\Events\Registered;
.
.
.
    protected $listen = [
        Registered::class => [
            RegisteredListener::class,
        ],
    ];
.
.
.

4. 修改自带邮件内容

用户认证自带密码重置功能,当我们输入注册邮箱后,会给该邮箱发送密码重置邮件。如何修改该邮件视图呢?

app/Http/Controllers/Auth/ForgotPasswordController.php 中使用 Illuminate\Foundation\Auth\SendsPasswordResetEmails trait 来实现该功能。

找到该文件:

.
.
.
public function sendResetLinkEmail(Request $request)
    {
        $this->validateEmail($request);

        // We will send the password reset link to this user. Once we have attempted
        // to send the link, we will examine the response then see the message we
        // need to show to the user. Finally, we'll send out a proper response.
        $response = $this->broker()->sendResetLink(
            $request->only('email')
        );

        return $response == Password::RESET_LINK_SENT
                    ? $this->sendResetLinkResponse($response)
                    : $this->sendResetLinkFailedResponse($request, $response);
    }
.
.
.

由对应的 broker 来发送重置密码的链接。该 broker 正是 Illuminate\Auth\Passwords\PasswordBroker

.
.
.
    public function sendResetLink(array $credentials)
    {
        // First we will check to see if we found a user at the given credentials and
        // if we did not we will redirect back to this current URI with a piece of
        // "flash" data in the session to indicate to the developers the errors.
        $user = $this->getUser($credentials);

        if (is_null($user)) {
            return static::INVALID_USER;
        }

        // Once we have the reset token, we are ready to send the message out to this
        // user with a link to reset their password. We will then redirect back to
        // the current URI having nothing set in the session to indicate errors.
        $user->sendPasswordResetNotification(
            $this->tokens->create($user)
        );

        return static::RESET_LINK_SENT;
    }
.
.
.

调用了 User 类的 sendPasswordResetNotification 方法,并将 token 作为参数传进去。

回到模型 app/Models/User.php 中,它继承自 Illuminate\Foundation\Auth\User,而该类使用了 CanResetPassword trait,我们找到了最终调用的方法:

.
.
.
use Illuminate\Auth\Notifications\ResetPassword as ResetPasswordNotification;

trait CanResetPassword
{
    /**
     * Get the e-mail address where password reset links are sent.
     *
     * @return string
     */
    public function getEmailForPasswordReset()
    {
        return $this->email;
    }

    /**
     * Send the password reset notification.
     *
     * @param  string  $token
     * @return void
     */
    public function sendPasswordResetNotification($token)
    {
        $this->notify(new ResetPasswordNotification($token));
    }
}
.
.
.

所以要修改邮件内容也很简单了,在 User 类中重写该方法,自定义通知并发送就可以了。