Udemy - Nuxt JS with Laravel API - Building SSR Vue JS Apps 笔记17 Laravel Nuxt–Password Forgot and Reset

实现重置密码逻辑:

用户输入邮箱,点击发送重置邮件到本邮箱按钮,然后后端检测成功后,发送重置邮件给该邮箱。后端响应一个已发送邮件信息给用户。

后端部分:

api.php中添加route:

批注 2020-05-16 215644

新建这个PasswordResetController及添加create方法:

执行:

php artisan make:controller PasswordResetController

用一个类来记录重置的请求信息,创建一个PasswordReset类 及 对应的migration文件:

执行:

php artisan make:model PasswordReset -m

PasswordReset:

<?php

namespace App;

use Illuminate\Database\Eloquent\Model;

class PasswordReset extends Model
{
    protected $fillable = ['email', 'token'];
}

CreatePasswordResetsTable:

<?php

use Illuminate\Database\Migrations\Migration;
use Illuminate\Database\Schema\Blueprint;
use Illuminate\Support\Facades\Schema;

class CreatePasswordResetsTable extends Migration
{
    /**
     * Run the migrations.
     *
     * @return void
     */
    public function up()
    {
        Schema::create('password_resets', function (Blueprint $table) {
            $table->id();
            $table->string('email')->index();
            $table->string('token');
            $table->timestamps();
        });
    }

    /**
     * Reverse the migrations.
     *
     * @return void
     */
    public function down()
    {
        Schema::dropIfExists('password_resets');
    }
}

执行:

php artisan migrate

另外重置请求后,要给该用户发送一个通知邮件包含token,用户点击后即可重置密码。

新建一个名为PasswordResetRequestNotification的类:

执行:

php artisan make:notification PasswordResetRequestNotification

PasswordResetRequestNotification:

<?php

namespace App\Notifications;

use Illuminate\Bus\Queueable;
use Illuminate\Contracts\Queue\ShouldQueue;
use Illuminate\Notifications\Messages\MailMessage;
use Illuminate\Notifications\Notification;

class PasswordResetRequestNotification extends Notification
{
    use Queueable;

    protected $token;

    /**
     * Create a new notification instance.
     *
     * @param $token
     */
    public function __construct($token)
    {
        //
        $this->token = $token;
    }

    /**
     * Get the notification's delivery channels.
     *
     * @param mixed $notifiable
     * @return array
     */
    public function via($notifiable)
    {
        return ['mail'];
    }

    /**
     * Get the mail representation of the notification.
     *
     * @param mixed $notifiable
     * @return \Illuminate\Notifications\Messages\MailMessage
     */
    public function toMail($notifiable)
    {
        $url = url('http://localhost:3000/password/find/' . $this->token);
        return (new MailMessage)
            ->line('You are receiving this email because we received a password reset request for your account.')
            ->action('Reset Password', url($url))
            ->line('If you did not request a password reset, no further action is required');
    }

    /**
     * Get the array representation of the notification.
     *
     * @param mixed $notifiable
     * @return array
     */
    public function toArray($notifiable)
    {
        return [
            //
        ];
    }
}

最后PasswordResetController.php:

<?php

namespace App\Http\Controllers;

use App\Notifications\PasswordResetRequestNotification;
use App\PasswordReset;
use App\User;
use Illuminate\Http\Request;
use Illuminate\Support\Str;

class PasswordResetController extends Controller
{
    public function create(Request $request)
    {
        $request->validate([
            'email' => 'required|string|email',
        ]);
        $user = User::where('email', $request->get('email'))->first();
        if (!$user) {
            return response()->json([
                'message' => 'We cant find a user with that e-mail address.',
            ], 404);
        }
        $passwordReset = PasswordReset::updateOrCreate(
            ['email' => $user->email],
            [
                'email' => $user->email,
                'token' => Str::random(60),
            ]
        );

        //如果确有重置,发通知。
        if ($user && $passwordReset) {
            $user->notify(
                new PasswordResetRequestNotification($passwordReset->token)
            );
        }

        return response()->json(
            [
                'message' => 'We have e-mailed your password reset link!',
            ]
        );
    }
}

.env配置一下mailtrap.io的信息:

没有的 自行注册 https://mailtrap.io/ 

批注 2020-05-16 220041

Postman测试一下发送重置请求:

批注 2020-05-16 220109

打开mailtrap看刚刚发送的邮件:

批注 2020-05-16 220213

点击Reset Password按钮或下面的连接复制到窗口打开:

批注 2020-05-16 220258

现在实现前端的输入邮箱点击重置按钮页面逻辑:

新建pages/password/forgot.vue:

<template>
  <div class="container col-md-6 mt-5">
    <h2>Ask for password reset link</h2>
    <br>
    <div v-if="loading" class="alert alert-primary">
      Please wait...
    </div>
    <div class="alert alert-primary" v-if="messages.length>0">
      We have send you password reset link. Please check your email.
    </div>
    <form @submit.prevent="submit">
      <div class="form-group">
        <label>Email Address</label>
        <input type="email" class="form-control" v-model.trim="form.email"
               placeholder="Enter Email"
               autofocus>
        <small class="form-text text-danger" v-if="errors.email">
          {{errors.email[0]}}
        </small>
      </div>
      <button type="submit" class="btn btn-primary">Send Password Reset Link</button>
    </form>
    <br>
    <p>
      <nuxt-link to="/login">Back to Login</nuxt-link>
    </p>
  </div>
</template>

<script>
  export default {
    middleware: ['guest'],
    name: "forgot.vue",
    data() {
      return {
        form: {
          email: '',
        },
        messages: [],
        loading: false,
      }
    },
    methods: {
      async submit() {
        this.loading = true;
        this.messages = [];
        let {message} = await this.$axios.$post(`/password/create`, {
          email: this.form.email,
        });

        this.messages.push(message);
        this.loading = false;
        this.form = {};
      },
    }
  }
</script>

<style scoped>

</style>

注意中间件用了guest 如果已经登录了的,直接跳转dashboard,不展示重置页面给已经登录了的用户。

执行

npm run dev

打开  http://localhost:3000/password/forgot

批注 2020-05-16 223030

尝试请求:

批注 2020-05-16 223125

批注 2020-05-16 223152

查看mailtrap:

批注 2020-05-16 223227


下面来实现find的逻辑:

需要验证这个token是不是有效的。

api.php添加一个route:

批注 2020-05-16 220534

PasswordResetController添加这个find方法:

批注 2020-05-16 221424

Postman测试结果:

批注 2020-05-16 221557

如果失败:

批注 2020-05-16 221629

跳转重置输入密码页面的逻辑:

新建pages/password/find/_token.vue文件:

批注 2020-05-16 223339

<template>
  <div class="container col-md-6 mt-5">
    <h2>Update your password</h2>
    <br>
    <div class="alert alert-primary" v-for="message in messages">
      {{message}}
    </div>
    <form @submit.prevent="submit">
      <div class="form-group">
        <label>Email address</label>
        <input type="email" class="form-control" v-model.trim="form.email"
               placeholder="Enter email">
        <small class="form-text text-danger" v-if="errors.email">{{errors.email[0]}}</small>
      </div>

      <div class="form-group">
        <label>New Password</label>
        <input type="password" class="form-control" v-model.trim="form.password"
               placeholder="Password">
        <small class="form-text text-danger" v-if="errors.password">{{errors.password[0]}}</small>
      </div>
      <button class="btn btn-primary" type="submit">Login with new password</button>
    </form>
    <br>
    <p>Already have an account?
      <nuxt-link to="/login">Login</nuxt-link>
    </p>
  </div>
</template>

<script>
  export default {
    middleware: ['guest'],
    name: "_token",
    data() {
      return {
        form: {
          email: '',
          password: '',
          token: '',
        },
        messages: [],
        foundUserByToken: false,
      }
    },
    methods: {
      async submit() {

        this.messages = [];
        //根据token找用户
        let response = await this.$axios.$get(`/password/find/${this.form.token}`)
        console.log('Response:', response);
        this.foundUserByToken = true;
        this.messages.push("We found you...just a second!");

        if (this.foundUserByToken) {
          //前往重置密码
          let response = await this.$axios.$post('/password/reset', {
            email: this.form.email,
            password: this.form.password,
            token: this.form.token,
          });
          console.log('password reset is done: ', response);
          this.messages.push("Password reset success!.. just a second!");

          //登录用户

          await this.$auth.loginWith('local', {
            data: {
              email: this.form.email,
              password: this.form.password,
            }
          })

          this.messages.push("All Done! Redirecting...")

          //

          this.messages = [];
          this.form = {};
          //redirect
          this.$router.push('/topics');

        }

      }
    },
    mounted() {
      this.form.token = this.$route.params.token;
      console.log(this.form.token);
    }
  }
</script>

<style scoped>

</style>

点击Reset Password按钮后,将会跳转这个vue代码架设的页面:

批注 2020-05-16 225157

现在处理这个请求的后端逻辑:

批注 2020-05-16 225251

api.php中添加路由:

批注 2020-05-16 230154

重置完成后,需要邮件通知用户密码重置成功

新建一个通知:

执行:

php artisan make:notification PasswordResetSuccessNotification

PasswordResetSuccessNotification:

<?php

namespace App\Notifications;

use Illuminate\Bus\Queueable;
use Illuminate\Contracts\Queue\ShouldQueue;
use Illuminate\Notifications\Messages\MailMessage;
use Illuminate\Notifications\Notification;

class PasswordResetSuccessNotification extends Notification
{
    use Queueable;

    protected $passwordReset;

    /**
     * Create a new notification instance.
     *
     * @param $passwordReset
     */
    public function __construct($passwordReset)
    {
        //
        $this->passwordReset = $passwordReset;
    }

    /**
     * Get the notification's delivery channels.
     *
     * @param mixed $notifiable
     * @return array
     */
    public function via($notifiable)
    {
        return ['mail'];
    }

    /**
     * Get the mail representation of the notification.
     *
     * @param mixed $notifiable
     * @return \Illuminate\Notifications\Messages\MailMessage
     */
    public function toMail($notifiable)
    {
        return (new MailMessage)
            ->line('You have changed your password succefully.')
            ->line('If you did change password, no further action is required.')
            ->line('If you did not change password, protect your account.');
    }

    /**
     * Get the array representation of the notification.
     *
     * @param mixed $notifiable
     * @return array
     */
    public function toArray($notifiable)
    {
        return [
            //
        ];
    }
}

PasswordResetController

<?php

namespace App\Http\Controllers;

use App\Notifications\PasswordResetRequestNotification;
use App\Notifications\PasswordResetSuccessNotification;
use App\PasswordReset;
use App\User;
use Illuminate\Http\Request;
use Illuminate\Support\Carbon;
use Illuminate\Support\Facades\Hash;
use Illuminate\Support\Str;

class PasswordResetController extends Controller
{
    public function create(Request $request)
    {
        $request->validate([
            'email' => 'required|string|email',
        ]);

        $user = User::where('email', $request->get('email'))->first();
        if (!$user) {
            return response()->json([
                'message' => 'We cant find a user with that e-mail address.',
            ], 404);
        }
        $passwordReset = PasswordReset::updateOrCreate(
            ['email' => $user->email],
            [
                'email' => $user->email,
                'token' => Str::random(60),
            ]
        );

        //如果确有重置,发通知。
        if ($user && $passwordReset) {
            $user->notify(
                new PasswordResetRequestNotification($passwordReset->token)
            );
        }

        return response()->json(
            [
                'message' => 'We have e-mailed your password reset link!',
            ]
        );
    }

    // find the token password reset
    public function find($token)
    {
        $passwordReset = PasswordReset::where('token', $token)->first();
        //token没找到
        if (!$passwordReset) {
            return response()->json(
                [
                    'message' => 'This password reset token is invalid',
                ],
                404
            );
        }

        //判断重置链接是否已经发布超过720分钟了
        if (Carbon::parse($passwordReset->updated_at)->addMinutes(720)->isPast()) {
            //超时失效
            $passwordReset->delete();

            return response()->json(
                [
                    'message' => 'This password reset token is invalid.',
                ],
                404
            );
        }

        //返回该PasswordReset模型实例
        return response()->json($passwordReset);
    }

    //重置密码
    public function reset(Request $request)
    {
        $request->validate([
            'email' => 'required|string|email',
            'password' => 'required|string|min:6',
            'token' => 'required|string',
        ]);

        $passwordReset = PasswordReset::where([
            ['token', $request->get('token')],
            ['email', $request->get('email')],
        ])->first();

        if (!$passwordReset) {
            return response()->json([
                'message' => 'This password reset token is invalid.',
            ], 404);
        }

        $user = User::where('email', $passwordReset->email)->first();
        if (!$user) {
            return response()->json([
                'message' => 'We cant find a user with that e-mail address.',
            ], 404);
        }
        $user->password = Hash::make($request->get('password'));
        $user->save();

        //数据库中删除reset 实例数据
        $passwordReset->delete();

        //通知用户密码重置成功
        $user->notify(new PasswordResetSuccessNotification($passwordReset));

        return response()->json($user);
    }
}

效果:

nuxt-password-reset

批注 2020-05-16 231016

批注 2020-05-16 231147

源码:

后端:

https://github.com/dzkjz/laravel-backend-nuxt-frontend

前端:

https://github.com/dzkjz/laravel-backend-nuxt-frontend-frontpart

原教程代码:

Source code download

Download link for all the projects we build in this course.

https://github.com/kaloraat/laravel-nuxt-udemy/

Source code with password forgot/reset

Source code with password forgot and reset. Implemented on both backend and frontend. Make sure to update .env with your own email settings or you can use free email service by mailtrap.io for development purpose:

https://github.com/kaloraat/laravel_nuxt

posted @ 2020-05-16 23:16  dzkjz  阅读(125)  评论(0)    收藏  举报