Vue SPA 的 Laravel 电子邮件验证
Posted
技术标签:
【中文标题】Vue SPA 的 Laravel 电子邮件验证【英文标题】:Laravel Email Verification for Vue SPA 【发布时间】:2019-12-16 06:56:48 【问题描述】:如何使用 Vue Router 在 Vue SPA 上实现 Laravel 的电子邮件验证?
到目前为止,我已尝试通过更改 VerificationController 的验证和重新发送方法来处理电子邮件验证。然后,我创建了一个新通知并添加了用于验证的 API 路由。
当生成验证链接并发送到用户的电子邮件时,验证 url 类似于:
https://foobar.test/email/verify/1?expires=1565276056&signature=b15ccd7d6198bdcf81eea4f5cb441efe8eb2d6d5b57a1ce0b1171e685613d917
当链接被点击时,它会打开一个页面,但它在后端什么也不做,因为@verify api 路由没有被命中。
有什么建议吗?
验证控制器.php
<?php
namespace App\Http\Controllers\Auth;
use App\User;
use Illuminate\Http\Request;
use App\Http\Controllers\Controller;
use Illuminate\Foundation\Auth\VerifiesEmails;
use Illuminate\Validation\ValidationException;
class VerificationController extends Controller
/*
|--------------------------------------------------------------------------
| Email Verification Controller
|--------------------------------------------------------------------------
|
| This controller is responsible for handling email verification for any
| user that recently registered with the application. Emails may also
| be re-sent if the user didn't receive the original email message.
|
*/
use VerifiesEmails;
/**
* Where to redirect users after verification.
*
* @var string
*/
protected $redirectTo = '/home';
/**
* Create a new controller instance.
*
* @return void
*/
public function __construct()
$this->middleware('auth:api');
$this->middleware('signed')->only('verify');
$this->middleware('throttle:600,1')->only('verify', 'resend');
/**
* Show the email verification notice.
*
*/
public function show()
//
/**
* Mark the authenticated user's email address as verified.
*
* @param \Illuminate\Http\Request $request
* @return \Illuminate\Http\Response
*/
public function verify(Request $request)
$userID = $request[‘id’];
$user = User::findOrFail($userID);
$user->email_verified_at = date("Y-m-d g:i:s");
$user->save();
return response()->json('Email verified!');
/**
* Resend the email verification notification.
*
* @param \Illuminate\Http\Request $request
* @return \Illuminate\Http\Response
*/
public function resend(Request $request)
if ($request->user()->hasVerifiedEmail())
return response()->json('The email is already verified.', 422);
$request->user()->sendEmailVerificationNotification();
return response()->json('We have e-mailed your verification link!');
验证电子邮件.php
<?php
namespace App\Notifications;
use Illuminate\Notifications\Notification;
use Illuminate\Support\Facades\URL;
use Carbon\Carbon;
use Illuminate\Auth\Notifications\VerifyEmail as VerifyEmailBase;
class VerifyEmail extends VerifyEmailBase
/**
* Get the verification URL for the given notifiable.
*
* @param mixed $notifiable
* @return string
*/
protected function verificationUrl($notifiable)
return URL::temporarySignedRoute(
‘verification.verify’, Carbon::now()->addMinutes(60), [‘id’ => $notifiable->getKey()]
);
API.php
Route::get('email/verify/id', 'Auth\VerificationController@verify')->name('verification.verify');
Route::get('email/resend', 'Auth\VerificationController@resend')->name('verification.resend');
【问题讨论】:
您可能错过了将implements MustVerifyEmail
添加到那个。如果缺少此接口实现,邮件检查方法将不会做任何事情。你能发布或检查你的用户模型吗?
为什么你认为它没有被击中?它完全匹配。你的 API 路径上有前缀吗?
【参考方案1】:
这是我为解决问题所做的。转到 AuthServiceProvider
/**
* Register any authentication / authorization services.
*
* @return void
*/
public function boot()
$this->registerPolicies();
//
VerifyEmail::createUrlUsing(function ($notifiable)
$params = [
"expires" => Carbon::now()
->addMinutes(60)
->getTimestamp(),
"id" => $notifiable->getKey(),
"hash" => sha1($notifiable->getEmailForVerification()),
];
ksort($params);
// then create API url for verification. my API have `/api` prefix,
// so i don't want to show that url to users
$url = \URL::route("verification.verify", $params, true);
// get APP_KEY from config and create signature
$key = config("app.key");
$signature = hash_hmac("sha256", $url, $key);
// generate url for yous SPA page to send it to user
return env("APP_FRONT") .
"/auth/verify-email/" .
$params["id"] .
"/" .
$params["hash"] .
"?expires=" .
$params["expires"] .
"&signature=" .
$signature;
);
将此添加到 api.php
Route::get("/verify-email/id/hash", [
VerifyEmailController::class,
"__invoke",
])
->middleware(["auth:sanctum","signed", "throttle:6,1"])
->name("verification.verify");
将此添加到 VerifyEmailController.php
/**
* Mark the authenticated user's email address as verified.
*
* @param \Illuminate\Foundation\Auth\EmailVerificationRequest $request
* @return \Illuminate\Http\RedirectResponse
*/
public function __invoke(EmailVerificationRequest $request)
if ($request->user()->hasVerifiedEmail())
return response()->json(
[
"message" => "Your'r email already verified.",
],
Response::HTTP_BAD_REQUEST
);
if ($request->user()->markEmailAsVerified())
event(new Verified($request->user()));
return response()->json(
[
"message" => "Verification complete thank you.",
],
Response::HTTP_OK
);
前端
async verfyEmail()
try
const params = new URLSearchParams(this.$route.query)
let res = await this.$axios.get(
'verify-email/' +
this.$route.params.id +
'/' +
this.$route.params.hash,
params
)
this.$router.push( name: 'platform-dashboard' )
catch (error)
console.log(error.response)
this.$router.push( name: 'platform-dashboard' )
【讨论】:
【参考方案2】:解决方案非常简单。
使用temporarySignedRoute
需要指定路由,默认为verification.verify
,过期时间和参数。
<?php
namespace App\Notifications;
use Illuminate\Notifications\Notification;
use Illuminate\Support\Facades\URL;
use Carbon\Carbon;
use Illuminate\Auth\Notifications\VerifyEmail as VerifyEmailBase;
class VerifyEmail extends VerifyEmailBase
/**
* Get the verification URL for the given notifiable.
*
* @param mixed $notifiable
* @return string
*/
protected function verificationUrl($notifiable)
return URL::temporarySignedRoute(
'verification.verify',
now()->addMinutes(60),
['id' => $notifiable->id, 'hash' => sha1($notifiable->getEmailForVerification())]
);
【讨论】:
这将创建注册用户收到的电子邮件中的 URL,但没有解释如何在 VueJS 前端处理它。正如我从上一个答案中了解到的那样,来自该 URL 的参数需要在控制器中处理,但由于示例使用 Angular,具体细节尚不清楚。【参考方案3】:我的 Angular SPA 遇到了同样的问题。不确定您是否仍需要帮助,但希望我的回答能对某人有所帮助。
所以虽然 laravel UrlGenerator::signedRoute
不够灵活(你可以订阅这个idea。不是同一种情况,但与此相关)我们必须自己实现 url 签名。
在您的VerifyEmail
班级中:
protected function verificationUrl($notifiable)
// collect and sort url params
$params = [
'expires' => Carbon::now()
->addMinutes(Config::get('auth.verification.expire', 60))
->getTimestamp(),
'id' => $notifiable->getKey(),
'hash' => sha1($notifiable->getEmailForVerification()),
];
ksort($params);
// then create API url for verification. my API have `/api` prefix,
// so i don't want to show that url to users
$url = URL::route(
'api:auth:verify',
$params,
true
);
// get APP_KEY from config and create signature
$key = config('app.key');
$signature = hash_hmac('sha256', $url, $key);
// generate url for yous SPA page to send it to user
return url('verify-email') . '?' . http_build_query($params + compact('signature'), false);
之后,在您的 SPA 中,您应该获取 url 参数并调用 API 请求。我将指定 Angular 示例,但它应该很容易适应 Vue。
// on component load
ngOnInit()
// get query params from current route
this.route.queryParamMap.subscribe(params =>
// generate API url. Make sure your query params come in the same order
// as in signature generation. By default signature check middleware
// extracts `signature` param so `expires` is the only param that
// is checked so order doesn't matter, but if you need another params -
// it can turn into a problem
const url = this.router.createUrlTree(['api', 'auth', 'verify', data.id, data.hash],
queryParams: expires: data.expires, signature: data.signature).toString();
// make API request. if signature check fails - you will receive 403 error
return this.http.get(url).subscribe();
);
我看到的另一种更简单的方法是生成直接 API url 并将其发送给用户。验证后只需将浏览器重定向到您的 SPA。我只是不明白为什么它在你的情况下不起作用。也许您的网络服务器配置中有一些重写规则,因此您的实际域名与您的APP_URL
不匹配?或者也许你在另一个端口提供你的 API?
【讨论】:
以上是关于Vue SPA 的 Laravel 电子邮件验证的主要内容,如果未能解决你的问题,请参考以下文章
使用内置的 Laravel 5.2 身份验证并加载 SPA,然后为所有其他路由加载 Dingo API
如果页面仅加载一次,如何在 SPA 中获取 Laravel cookie