Laravel API, использующий измененную черту MustVerifyEmail, не может получить запрос пользователя - PullRequest
0 голосов
/ 09 января 2020

Я использую 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 не использовал его по умолчанию на всех моих контроллерах.

Итак, вот шаги, которые я выполнил:

  1. Создал собственное промежуточное ПО и прикрепил его ко всей группе промежуточного ПО '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')

ЛЮБОЕ РУКОВОДСТВО БЫЛО ОЧЕНЬ ЦЕННО!

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...