Я использую Laravel 6.x в качестве бэкэнда с внешним (другим доменом) Vue внешним интерфейсом и не обладаю функциональностью регистрации пользователя. Я регистрирую пользователей, импортируя группы пользователей с помощью пакета Maatwebsite / Laravel -Excel - который прекрасно работает.
Таким образом, когда каждый пользователь создается, создается работа, отправляя каждому пользователю ссылку для проверки электронной почты. , который при первом входе в систему должен будет поменять свой пароль, и одновременно электронная почта будет помечена как проверенная, что также должно работать нормально.
Проблема в том, что с уже созданной фабрикой пользователей, которые заполнить их поле email_verified_at - и новых импортированных пользователей - я не могу войти, так как пользовательское промежуточное ПО EnsureEmailApiIsVerified не имеет доступа к $ request-> user (). Я выяснил, что могу указать аутентификацию «api», например, $ request-> user («api»), которая затем может подобрать пользователя, но только его токен на предъявителя (используя Laravel Passport) отправляется с запрос.
Это имеет смысл, так как иначе система узнает, кто пользователь запроса, без какого-либо идентификатора, такого как токен. Но тогда как стандарт «реализует MustVerifyEmail» на модели «Пользователь» и последующее стандартное промежуточное ПО «EnsureEmailIsVerified» на веб-маршрутах подхватывает $ request-> user ()?
Разумеется, либо (стандартное и пользовательское) промежуточное ПО должно иметь доступ к $ request-> user () или оба не должны.
Теперь мне пришлось изменить и вывести довольно много контроллеров фреймворка в мой каталог App \ Http но я скопировал их почти дословно, просто изменив несколько вещей, чтобы убедиться, что он работает с моими маршрутами API - потому что в config / auth было установлено значение защиты по умолчанию «api» вместо «web». php не использовал его по умолчанию на всех моих контроллерах.
Итак, вот шаги, которые я выполнил:
- Создал собственное промежуточное ПО и прикрепил его ко всей группе промежуточного ПО 'api' в App \ Http \ Kernel. php
/**
* The application's route middleware groups.
*
* @var array
*/
protected $middlewareGroups = [
'web' => [
\App\Http\Middleware\EncryptCookies::class,
\Illuminate\Cookie\Middleware\AddQueuedCookiesToResponse::class,
\Illuminate\Session\Middleware\StartSession::class,
// \Illuminate\Session\Middleware\AuthenticateSession::class,
\Illuminate\View\Middleware\ShareErrorsFromSession::class,
\App\Http\Middleware\VerifyCsrfToken::class,
\Illuminate\Routing\Middleware\SubstituteBindings::class
],
'api' => [
'throttle:60,1',
'bindings',
'verifiedapi',
],
];
/**
* The application's route middleware.
*
* These middleware may be assigned to groups or used individually.
*
* @var array
*/
protected $routeMiddleware = [
'auth' => \App\Http\Middleware\Authenticate::class,
'auth.basic' => \Illuminate\Auth\Middleware\AuthenticateWithBasicAuth::class,
'bindings' => \Illuminate\Routing\Middleware\SubstituteBindings::class,
'cache.headers' => \Illuminate\Http\Middleware\SetCacheHeaders::class,
'can' => \Illuminate\Auth\Middleware\Authorize::class,
'guest' => \App\Http\Middleware\RedirectIfAuthenticated::class,
'password.confirm' => \Illuminate\Auth\Middleware\RequirePassword::class,
'role' => \App\Http\Middleware\HasRole::class,
'signed' => \Illuminate\Routing\Middleware\ValidateSignature::class,
'throttle' => \Illuminate\Routing\Middleware\ThrottleRequests::class,
'verified' => \Illuminate\Auth\Middleware\EnsureEmailIsVerified::class,
'verifiedapi' => \App\Http\Middleware\EnsureApiEmailIsVerified::class,
];
Тогда вот пользовательское промежуточное ПО EnsureApiEmailIsVerified:
<?php
namespace App\Http\Middleware;
use Closure;
use App\Http\Controllers\API\Auth\MustVerifyApiEmail;
class EnsureApiEmailIsVerified
{
/**
* Handle an incoming request.
*
* @param \Illuminate\Http\Request $request
* @param \Closure $next
* @return mixed
*/
public function handle($request, Closure $next)
{
if (! $request->user('api') ||
($request->user('api') instanceof MustVerifyApiEmail &&
! $request->user('api')->hasVerifiedEmail())) {
return abort(403, 'Your email has not yet been verified.');
}
return $next($request);
}
}
. смотрите, что ссылается на экземпляр моего пользовательского «MustVerifyApiEmail», который является признаком, который используется в пользовательской модели, с единственным отклонением от стандартного признака, являющимся функцией publi c sendApiEmailVerificationNotification, как таковой:
<?php
namespace App\Http\Controllers\API\Auth;
use App\Notifications\VerifyApiEmail;
trait MustVerifyApiEmail
{
/**
* Determine if the user has verified their email address.
*
* @return bool
*/
public function hasVerifiedEmail()
{
return ! is_null($this->email_verified_at);
}
/**
* Mark the given user's email as verified.
*
* @return bool
*/
public function markEmailAsVerified()
{
return $this->forceFill([
'email_verified_at' => $this->freshTimestamp(),
])->save();
}
/**
* Send the email verification notification.
*
* @return void
*/
public function sendApiEmailVerificationNotification()
{
$this->notify(new VerifyApiEmail);
}
/**
* Get the email address that should be used for verification.
*
* @return string
*/
public function getEmailForVerification()
{
return $this->email;
}
}
Этот новый 'sendApiEmailVerificationNotification ()' уведомляет пользователя $ request ('api') с помощью пользовательского уведомления VerifyApiEmail следующим образом:
<?php
namespace App\Notifications;
use Illuminate\Bus\Queueable;
use Illuminate\Support\Carbon;
use Illuminate\Support\Facades\URL;
use Illuminate\Support\Facades\Lang;
use Illuminate\Support\Facades\Config;
use Illuminate\Notifications\Notification;
use Illuminate\Contracts\Queue\ShouldQueue;
use Illuminate\Notifications\Messages\MailMessage;
class VerifyApiEmail implements ShouldQueue
{
use Queueable;
/**
* The callback that should be used to build the mail message.
*
* @var \Closure|null
*/
public static $toMailCallback;
/**
* Create a new notification instance.
*
* @return void
*/
public function __construct()
{
//
}
/**
* Get the notification's delivery channels.
*
* @param mixed $notifiable
* @return array
*/
public function via($notifiable)
{
return ['mail'];
}
/**
* Build the mail representation of the notification.
*
* @param mixed $notifiable
* @return \Illuminate\Notifications\Messages\MailMessage
*/
public function toMail($notifiable)
{
$verificationUrl = $this->verificationUrl($notifiable);
if (static::$toMailCallback) {
return call_user_func(static::$toMailCallback, $notifiable, $verificationUrl);
}
return (new MailMessage)
->subject(Lang::get('Verify Email Address'))
->line(Lang::get('Please click the button below to verify your email address.'))
->action(Lang::get('Verify Email Address'), $verificationUrl)
->line(Lang::get('If you did not create an account, no further action is required.'));
}
/**
* Get the verification URL for the given notifiable.
*
* @param mixed $notifiable
* @return string
*/
protected function verificationUrl($notifiable)
{
return URL::temporarySignedRoute(
'verification.api.verify',
Carbon::now()->addMinutes(Config::get('auth.verification.expire', 60)),
[
'id' => $notifiable->getKey(),
'hash' => sha1($notifiable->getEmailForVerification()),
]
);
}
/**
* Set a callback that should be used when building the notification mail message.
*
* @param \Closure $callback
* @return void
*/
public static function toMailUsing($callback)
{
static::$toMailCallback = $callback;
}
}
Новые маршруты API следующие:
Route::namespace('API\Auth')->group(function () {
Route::post('login', 'PassportController@login');
Route::post('refresh', 'PassportController@refresh');
Route::post('logout', 'PassportController@logout');
Route::get('email/verify/{id}/{hash}', 'VerificationApiController@verify')->name('verification.api.verify');
Route::get('email/resend', 'VerificationApiController@resend')->name('api.verification.resend');
});
И VerificationApiController выглядит следующим образом:
<?php
namespace App\Http\Controllers\API\Auth;
use Illuminate\Http\Request;
use App\Http\Controllers\Controller;
use Illuminate\Auth\Events\Verified;
use Illuminate\Auth\Access\AuthorizationException;
class VerificationApiController extends Controller
{
/**
* Show the email verification notice.
*
* @param \Illuminate\Http\Request $request
* @return \Illuminate\Http\Response
*/
public function show(Request $request)
{
//
}
/**
* Mark the authenticated user's email address as verified.
*
* @param \Illuminate\Http\Request $request
* @return \Illuminate\Http\Response
*
* @throws \Illuminate\Auth\Access\AuthorizationException
*/
public function verify(Request $request)
{
if (! hash_equals((string) $request->route('id'), (string) $request->user('api')->getKey())) {
throw new AuthorizationException;
}
if (! hash_equals((string) $request->route('hash'), sha1($request->user('api')->getEmailForVerification()))) {
throw new AuthorizationException;
}
if ($request->user('api')->hasVerifiedEmail()) {
return response()->json(['error' => 'Email already verified'], 422);
}
if ($request->user('api')->markEmailAsVerified()) {
event(new Verified($request->user('api')));
}
return response()->json(['success' => 'Email verified!']);
}
/**
* Resend the email verification notification.
*
* @param \Illuminate\Http\Request $request
* @return \Illuminate\Http\Response
*/
public function resend(Request $request)
{
$request->user('api')->sendApiEmailVerificationNotification();
return response()->json(['success' => 'Email verification has been resent!']);
}
}
Я также заметил, что в Модели пользователя он расширяет Пользователь как Authenticatable, который затем использует стандартную MustVerifyEmail ', поэтому я вынес это из Framework также изменил использование на новый MustVerifyApiEmail - примерно так:
<?php
namespace App\Http\Controllers\API\Auth;
use Illuminate\Auth\Authenticatable;
use Illuminate\Database\Eloquent\Model;
use Illuminate\Auth\Passwords\CanResetPassword;
use Illuminate\Foundation\Auth\Access\Authorizable;
use App\Http\Controllers\API\Auth\MustVerifyApiEmail;
use Illuminate\Contracts\Auth\Authenticatable as AuthenticatableContract;
use Illuminate\Contracts\Auth\Access\Authorizable as AuthorizableContract;
use Illuminate\Contracts\Auth\CanResetPassword as CanResetPasswordContract;
class User extends Model implements
AuthenticatableContract,
AuthorizableContract,
CanResetPasswordContract
{
use Authenticatable, Authorizable, CanResetPassword, MustVerifyApiEmail;
}
Моя модель User затем выглядит следующим образом:
<?php
namespace App;
use Illuminate\Support\Str;
use Laravel\Passport\HasApiTokens;
use Illuminate\Notifications\Notifiable;
use App\Http\Controllers\API\Auth\User as Authenticatable;
use App\Http\Controllers\API\Auth\MustVerifyApiEmailInterface;
class User extends Authenticatable implements MustVerifyApiEmailInterface
{
use HasApiTokens, Notifiable;
...
Как вы можете видеть, это довольно немного настройки - но все должно работать теоретически, и я не получаю ошибок, которые могу использовать. Вот ошибки, которые я получаю:
Когда я вхожу в систему с пользователем, чья электронная почта подтверждена или даже не подтверждена, я получаю сообщение об ошибке, что электронная почта пользователя не была проверена - но только потому, что она не выбирает до $ request-> user ('api'). Когда я пытаюсь выдать ошибку в самом промежуточном программном обеспечении перед возвратом запроса, сбрасывающего $ request-> user ('api'), это дает мне значение null
Так что мой вопрос со стандартным промежуточным программным обеспечением 'Verified' => \ Illuminate \ Auth \ Middleware \ EnsureEmailIsVerified :: class, - как это подхватывает $ request-> user ()?
Есть ли что-то, чего мне не хватает или я поступаю неправильно? Кажется, что когда пользователь входит в систему, он не входит в систему, а затем запускает промежуточное программное обеспечение - так что нет $ request-> user ('api') - возможно, потому что я использую Passport, но я думаю, что то, что должно случается, что промежуточное программное обеспечение должно работать после того, как оно аутентифицирует пользователя, тогда у него будет доступ к $ request-> user ('api')
ЛЮБОЕ РУКОВОДСТВО БЫЛО ОЧЕНЬ ЦЕННО!