Udemy - Nuxt JS with Laravel API - Building SSR Vue JS Apps 笔记17 Laravel Nuxt–Password Forgot and Reset
实现重置密码逻辑:
用户输入邮箱,点击发送重置邮件到本邮箱按钮,然后后端检测成功后,发送重置邮件给该邮箱。后端响应一个已发送邮件信息给用户。
后端部分:
api.php中添加route:
新建这个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/
Postman测试一下发送重置请求:
打开mailtrap看刚刚发送的邮件:
点击Reset Password按钮或下面的连接复制到窗口打开:
现在实现前端的输入邮箱点击重置按钮页面逻辑:
新建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
尝试请求:
查看mailtrap:
下面来实现find的逻辑:
需要验证这个token是不是有效的。
api.php添加一个route:
PasswordResetController添加这个find方法:
Postman测试结果:
如果失败:
跳转重置输入密码页面的逻辑:
新建pages/password/find/_token.vue文件:
<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代码架设的页面:
现在处理这个请求的后端逻辑:
api.php中添加路由:
重置完成后,需要邮件通知用户密码重置成功
新建一个通知:
执行:
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); } }
效果:
源码:
后端:
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
.envwith your own email settings or you can use free email service by mailtrap.io for development purpose:





















浙公网安备 33010602011771号