Laravel 8 休息 api 邮箱验证

Laravel 8 rest api email verification

在互联网和论坛中进行了大量搜索后,我放弃了...

我正在使用 Laravel 8 开发休息 api 并且我从本周开始尝试使用 officiel documentation 进行电子邮件验证工作,一旦用户注册,电子邮件总是成功发送 event(new Registered($user));
问题是,一旦我点击收到的电子邮件中的 link,我就会被重定向到登录页面(在本例中是 post 调用)..

这是我的 routes/api.php:

Route::group(['namespace' => 'App\Http\Controllers', 'middleware' => ['api'], 'prefix' => 'auth'], function ($router) {
    Route::post('login', 'AuthController@login')->name('login');
    Route::post('register', 'AuthController@register');
    Route::post('logout', 'AuthController@logout');
    Route::post('profile', 'AuthController@profile')->middleware('verified');
    Route::post('refresh', 'AuthController@refresh');
});

Route::group(['namespace' => 'App\Http\Controllers', 'middleware' => ['api']],function ($router) {
    Route::get('/email/verify/{id}/{hash}', 'VerificationController@verify')->middleware(['auth', 'signed'])->name('verification.verify');
    Route::get('/email/resend', 'VerificationController@resend')->middleware(['auth', 'throttle:6,1'])->name('verification.send');
});

这是我的 VerificationController:

<?php

namespace App\Http\Controllers;

use App\Http\Controllers\Controller;
use Illuminate\Http\Request;
use Illuminate\Foundation\Auth\EmailVerificationRequest;

class VerificationController extends Controller
{
    public function resend(Request $request)
    {
        $request->user()->sendEmailVerificationNotification();
        return response()->json(['message' => __('auth.email_sent')], Response::HTTP_NO_CONTENT);
    }

    public function verify(EmailVerificationRequest $request)
    {
        $request->fulfill();
        return response()->json(['message' => __('auth.user_verified_successfully')], Response::HTTP_RESET_CONTENT);
    }
}

最后但同样重要的是,我根据需要将 LogVerifiedUser 事件添加到 EventServiceProvider。

有什么建议吗?我试图从 verify 路由中删除中间件 auth,但它对我没有帮助...

PS: 我正在使用 JWT 进行身份验证

我不得不为我的休息开发完全相同的功能laravel 8 api,我与你分享我的工作,希望能够帮助你。

首先,您的问题是用户在点击验证后被重定向到登录页面 link。但问题是用户在点击时是否在数据库中被标记为已验证?

如果点击后在数据库中标记为已验证,则功能正常,但问题在于重定向。因为如果您使用的是 Rest API,您可能希望将用户重定向到前端应用程序的登录或成功页面。

最后一个问题是你的中间件。首先在 api.php 文件中,连接的中间件是 'auth:api' 而不是 'auth'。但是这一次你不必将中间件放在验证路径上,否则你将不得不让用户连接以便他验证他的电子邮件并且由于你通过 API 路径它非常无聊...

最后这是我选择的解决方案:

1.在你的 app/Models/User.php 实现 MustVerifyEmail (通常,根据我的理解,你已经做了,但我更愿意把它放在其他人经历这个主题的情况下)

<?php

namespace App\Models;

use Illuminate\Contracts\Auth\MustVerifyEmail;
use Illuminate\Database\Eloquent\Factories\HasFactory;
use Illuminate\Foundation\Auth\User as Authenticatable;
use Illuminate\Notifications\Notifiable;
use Laravel\Passport\HasApiTokens;

class User extends Authenticatable implements MustVerifyEmail
{
    use HasFactory, Notifiable, HasApiTokens;

    /**
     * The attributes that are mass assignable.
     *
     * @var array
     */
    protected $fillable = [
        'name',
        'email',
        'password',
    ];

    /**
     * The attributes that should be hidden for arrays.
     *
     * @var array
     */
    protected $hidden = [
        'password',
        'remember_token',
    ];

    /**
     * The attributes that should be cast to native types.
     *
     * @var array
     */
    protected $casts = [
        'email_verified_at' => 'datetime',
    ];
}

2。在你的 app/Http/Controllers/AuthController.php 上添加事件注册用户 (通常,据我了解,你已经这样做了,但我更愿意把它放在其他人经历这个的情况下主题)

<?php

namespace App\Http\Controllers;

use App\Models\User;
use Illuminate\Http\Request;
use Illuminate\Auth\Events\Registered;

class AuthController extends Controller
{
    public function register(Request $request)
    {
        $validatedData = $request->validate([
            'name' => 'required|max:55',
            'email' => 'email|required|unique:users',
            'password' => 'required|confirmed'
        ]);

        $validatedData['password'] = bcrypt($request->password);

        $user = User::create($validatedData);

        event(new Registered($user));

        $accessToken = $user->createToken('authToken')->accessToken;

        return response(['user' => $user, 'access_token' => $accessToken]);
    }

    public function login(Request $request)
    {
        $loginData = $request->validate([
            'email' => 'email|required',
            'password' => 'required'
        ]);

        if (!auth()->attempt($loginData)) {
            return response(['message' => 'Invalid Credentials']);
        }

        $accessToken = auth()->user()->createToken('authToken')->accessToken;

        return response(['user' => auth()->user(), 'access_token' => $accessToken]);
    }
}

3。在您的 routes/api.php 中定义了这条路线:


// Verify email
Route::get('/email/verify/{id}/{hash}', [VerifyEmailController::class, '__invoke'])
    ->middleware(['signed', 'throttle:6,1'])
    ->name('verification.verify');

// Resend link to verify email
Route::post('/email/verify/resend', function (Request $request) {
    $request->user()->sendEmailVerificationNotification();
    return back()->with('message', 'Verification link sent!');
})->middleware(['auth:api', 'throttle:6,1'])->name('verification.send');

4.创建app/Http/Controllers/VerifyEmailController.php

<?php

namespace App\Http\Controllers;

use Illuminate\Auth\Events\Verified;
use Illuminate\Http\RedirectResponse;
use Illuminate\Http\Request;
use Illuminate\Routing\Controller;
use App\Models\User;

class VerifyEmailController extends Controller
{

    public function __invoke(Request $request): RedirectResponse
    {
        $user = User::find($request->route('id'));

        if ($user->hasVerifiedEmail()) {
            return redirect(env('FRONT_URL') . '/email/verify/already-success');
        }

        if ($user->markEmailAsVerified()) {
            event(new Verified($user));
        }

        return redirect(env('FRONT_URL') . '/email/verify/success');
    }
}

说明:

有了这个解决方案,我们保留了所有通过电子邮件查看官方文档的操作。除了检查用户是否已连接以检索它并将他的电子邮件置于已验证之外。我们在控制器中启动一个方法,它将找到相应的用户并将其放入已验证。

我希望我能理解并能对您有所帮助:)

那是因为在register()方法中你没有在注册用户后立即登录用户,当用户点击邮件中的link时,laravel auth中间件检测到当前访问 link 的用户未通过身份验证,因此它将用户重定向到登录路由。解决此问题参考@Matthieu Gelle答案,但自定义如下:

在第 2 步中添加此代码

Auth::login($user);

低于event(new Registered($user));

在第 3 步中使用此中间件:

->middleware(['auth', 'signed'])->name('verification.verify');

对于那些使用sanctum的人:

->middleware(['auth:sanctum', 'signed'])->name('verification.verify');

并将方法名称从 '__invoke' 更改为 'verifyEmail'

在第 4 步中使用此方法:

public function verifyEmail(\Illuminate\Foundation\Auth\EmailVerificationRequest $request)
{
    $request->fulfill();
    return response()->json(['code' => 200, 'message' => "Verified successfully"], 200);
}