【Laravel實戰】實作Laravel會員驗證,看這一篇就夠了

【Laravel實戰】實作Laravel會員驗證,看這一篇就夠了

這個實作將使用 Jetstream 這個 Starter Kit 來進行快速開發

如果你想要了解更多關於 Jetstream 與其他驗證套件的比較,歡迎參考 3驗證系統開箱指南

功能介紹

Jetstream 內建了以下驗證常用的功能,讓你能夠快速實作:

登入表單

預設是使用 email 欄位來進行驗證登入,這張圖是我自定義進行的修改

註冊表單

預設是沒有 username 欄位的,這張圖是我自定義進行的修改

雙重驗證

這功能開啟後可由用戶自行決定是否要使用

重置密碼

電子郵件驗證

啟用此功能後,會在用戶完成註冊後進行 Email 驗證,必須完成驗證才能進入網站


準備Jetstream專案

新專案

Step1.下載&安裝套件

如果你正好要一併新增專案的話,可以在新增專案時順帶安裝 Jetstream

開啟 Terminal,輸入以下指令

laravel new 專案名稱 --jet

接著畫面會以問答的方式來詢問你要如何進行設定。

  • 問題1:你要使用哪一種前端框架? livewire 還是 inertia
  • 問題2:應用是否需要使用 teams 機制

這篇實戰將以對後端工程師最友善的 Livewire 來進行介紹,獨立進行開發沒有團隊,所以分別回答 0 以及 no

舊專案

如果你的專案已經存在,還是可以使用 Jetstream ,只是有些安裝步驟要自己作,也不難

Step 1.下載套件

開啟 Terminal,切換到專案資料夾目錄,輸入以下指令:

composer require laravel/jetstream

Step 2.安裝套件

輸入以下指令:

php artisan jetstream:install livewire

記得務必要輸入任何一種前端框架,比如 livewire,否則可是會報錯的!

如果你偏好使用 inertia 請把上方指令的 livewire 改成 inertia


安裝Jetstream

Step 1.安裝&編譯依賴前端套件

開啟 Terminal,輸入以下指令:

這一段指令會安裝所有依賴的前端套件,就類似安裝後端套件的 composer install

npm install

這一段指令會編譯所有依賴套件檔案,但不破壞其原本程式碼結構

npm run build

如果你的專案是 Laravel 9 ,可能會出現 Error: Cannot find module 'node:path' 的錯誤,解決關鍵是升級你的 Node.js 版本。具體作法是下載 Node.js 最新版本(v.18.10 以上)後安裝,並且到設定的進階環境變數,找到 PATH 去加入 Node.js 資料夾所在(C:\Program Files\nodejs),完成後關閉 CMD重開,輸入 node -v 確認版本是否升級到 v.18 ,順利的話問題就解決了

Step2.建立資料庫&生成資料表

別忘了要建立一個專案要使用的資料庫,並把帳號密碼等相關設定寫在.env檔裏頭

完成資料庫建立後,開啟 Terminal,輸入以下指令:

php artisan migrate

當一切執行完畢之後就完成了套件的下載以及安裝了,非常簡單

Step3(非必須).如果應用的網域並非根路徑,有需要進行 Livewire 設定

作法是使用以下指令來建立 livewire 設定

php artisan livewire:publish --config php artisan livewire:publish --assets

接著設定 config\livewire.php 裏頭的 asset_url 如下所示:

'asset_url' => 'http://localhost:6080/jetstream3/public',


基本自定義驗證

雖然 Jetstream 已經替你完成了驗證系統的開發,但你還是可以有很大的彈性來進行修改

修改視圖

所有的視圖都被放在 resources/views/auth 資料夾內,如下表,你可以根據需要來修改

檔名 對應畫面
confirm-password.blade.php 確認密碼
forgot-password.blade.php 忘記密碼
login.blade.php 登入頁面
register.blade.php 註冊頁面
reset-password.blade.php 重設密碼
two-factor-challenge.blade.php 二階段認證
verify-email.blade.php 驗證Email

修改邏輯

所有的後端驗證邏輯由 Laravel Fortify 提供支持,檔案存放於 app/Actions/Fortify 資料夾內,你可以根據下表來找到程式碼進行修改

檔名 對應功能
CreateNewUser.php 新增使用者,用於註冊時
PasswordValidationRules.php 密碼驗證規則
ResetUserPassword.php 重設使用者密碼
UpdateUserPassword.php 變更使用者密碼
UpdateUserProfileInformation.php 變更使用者資料

修改設定

所有的驗證設定都被存放於 config/fortify.php ,你可以根據專案需要來開啟或關閉以下功能:

'features' => [
    Features::registration(), //註冊功能
    Features::resetPasswords(), //重設密碼
    Features::emailVerification(), //Email驗證
    Features::updateProfileInformation(), //更新使用者資訊
    Features::updatePasswords(), //更新密碼
    Features::twoFactorAuthentication([ //二階段認證
        'confirmPassword' => true,
    ]),
],

比如當你把 Features::resetPasswords() 這一行註解起來之後,就會發現登入頁面不再提供忘記密碼的功能囉

修改登入驗證邏輯

雖然 Jetstream 已經完成了登入驗證的邏輯,也就是比對 username (預設是email欄位) 以及密碼是否正確,但你還是可以自己修改,只要編輯 app/Providers/FortifyServiceProvider.php 即可,如下範例所示:

//app\Providers\FortifyServiceProvider.php

namespace App\Providers;
...

class FortifyServiceProvider extends ServiceProvider
{

    public function boot()
    {
        ...
        Fortify::authenticateUsing(function (Request $request) {
            //撰寫你的登入驗證邏輯,最終必須回傳登入者的模型才表示登入成功
            $user = User::where('email', $request->email)->first();

            if ($user && Hash::check($request->password, $user->password)) {
                return $user;
            }
        });
    }
}

修改個人資料管理

Jetstream 內建有提供了使用者個人資料管理功能,允許使用者能夠便利的更新姓名,電子郵件地址和大頭照

視圖

個人資料管理功能視圖位於:resources/views/profile/update-profile-information-form.blade.php

但假如你用的是 Inertia ,則 vue 檔案可以在以下位置找到:resources/js/Pages/Profile/UpdateProfileInformationForm.vue

程式邏輯

如果你需要修改使用者更新個人資料的邏輯,可編輯以下檔案:

app/Actions/Fortify/UpdateUserProfileInformation.php

如果你想開啟使用者大頭照功能,只需要到 config/jetstream.php 設定檔去移除 Features::profilePhotos() 的註解即可

//config\jetstream.php

'features' => [
    Features::profilePhotos(),
    //Features::api(),
    // Features::teams(),
],

修改登入後轉址連結

開啟 app/Providers/RouteServiceProvider.php ,修改 HOME 常數到新的路徑即可

//app\Providers\RouteServiceProvider.php

namespace App\Providers;

class RouteServiceProvider extends ServiceProvider
{
    //用戶登入後要轉址的頁面
    public const HOME = '/dashboard';
}

常用自定義需求

改用帳號欄位來進行登入

Step 1.修改登入視圖

開啟 resources/views/auth/login.blade.php,將視圖內的 email 輸入項改成 username

//resources/views/auth/login.blade.php

<x-guest-layout>
    <x-jet-authentication-card>
        <x-slot name="logo">
            <x-jet-authentication-card-logo />
        </x-slot>

        <x-jet-validation-errors class="mb-4" />

        @if (session('status'))
            <div class="mb-4 font-medium text-sm text-green-600">
                {{ session('status') }}
            </div>
        @endif

        <form method="POST" action="{{ route('login') }}">
            @csrf

            <div>
                <x-jet-label for="username" value="帳號" />
                <x-jet-input id="username" class="block mt-1 w-full" type="text" name="username"
                    :value="old('username')"
                    required autofocus />
            </div>

            <div class="mt-4">
                <x-jet-label for="password" value="{{ __('Password') }}" />
                <x-jet-input id="password" class="block mt-1 w-full" type="password" name="password" required autocomplete="current-password" />
            </div>

            <div class="block mt-4">
                <label for="remember_me" class="flex items-center">
                    <x-jet-checkbox id="remember_me" name="remember" />
                    <span class="ml-2 text-sm text-gray-600">{{ __('Remember me') }}</span>
                </label>
            </div>

            <div class="flex items-center justify-end mt-4">
                @if (Route::has('password.request'))
                    <a class="underline text-sm text-gray-600 hover:text-gray-900" href="{{ route('password.request') }}">
                        {{ __('Forgot your password?') }}
                    </a>
                @endif

                <x-jet-button class="ml-4">
                    {{ __('Log in') }}
                </x-jet-button>
            </div>
        </form>
    </x-jet-authentication-card>
</x-guest-layout>
Step 2.修改驗證設定

修改驗證設定檔 config/fortify.php 裡頭的 username 設定,改成如下所示:

'username' => 'username',


追加註冊欄位

Step 1.修改註冊視圖

這裡示範追加 username 欄位,開啟 resources/views/auth/register.blade.php,加入 username 輸入項,如下範例所示:

//resources/views/auth/register.blade.php

<x-guest-layout>
    ...

        <form method="POST" action="{{ route('register') }}">
            @csrf

            <div>
                <x-jet-label for="username" value="{{ __('Username') }}" />
                <x-jet-input id="username" class="block mt-1 w-full" type="text" name="username" :value="old('username')" required
                    autofocus autocomplete="username" />
            </div>

            <div>
                <x-jet-label for="name" value="{{ __('Name') }}" />
                <x-jet-input id="name" class="block mt-1 w-full" type="text" name="name" :value="old('name')" required autofocus autocomplete="name" />
            </div>

        </form>
    </x-jet-authentication-card>
</x-guest-layout>
Step 2.修改新增使用者功能

開啟 app/Actions/Fortify/CreateNewUser.php ,修改共兩處的程式碼

//app\Actions\Fortify\CreateNewUser.php

namespace App\Actions\Fortify;

class CreateNewUser implements CreatesNewUsers
{
    use PasswordValidationRules;

    public function create(array $input)
    {
        Validator::make($input, [
            //增加這一行
            'username' => ['required', 'string', 'max:255'],
            'name' => ['required', 'string', 'max:255'],
            ...
        ])->validate();

        return User::create([
            //增加這一行
            'username' => $input['username'],
            'name' => $input['name'],
            ...
        ]);
    }
}
Step 3.擴充資料表

因為使用者資料存放於 users 表格內,因此需要擴增一個名為 username 的欄位

在這裡我們新增一個 Migration 檔案來執行追加欄位的作業。開啟 Terminal,輸入以下指令:

php artisan make:migration add_username_users_table --table=users

開啟剛生成的 Migration 檔案,將內容修改如下範例所示:

//database\migrations\xxxx_xx_xx_xxxxxx_add_username_users_table.php

use Illuminate\Database\Migrations\Migration;
use Illuminate\Database\Schema\Blueprint;
use Illuminate\Support\Facades\Schema;

class AddUsernameUsersTable extends Migration
{
    /**
     * Run the migrations.
     *
     * @return void
     */
    public function up()
    {
        Schema::table('users', function (Blueprint $table) {
            $table->string('username',30)->unique();
        });
    }

    /**
     * Reverse the migrations.
     *
     * @return void
     */
    public function down()
    {
        Schema::table('users', function (Blueprint $table) {
            $table->dropColumn('username');
        });
    }
}

編輯完後執行 migrate 來完成欄位新增

php artisan migrate

Step 4.加入User白名單

如果沒有把 username 加入白名單欄位的話,將無法正常的將 username 的值寫入資料庫中

開啟 app/Models/User.php ,將 username 加入 $fillable 陣列裡頭

//app\Models\User.php

protected $fillable = [
    'username', //加入這一行
    'name',
    'email',
    'password',
];

如果有使用 Voyager套件來製作後台功能的話,還需要在 User.php 加上以下內容才不會出現角色相關錯誤

//app\Models\User.php

use TCG\Voyager\Traits\VoyagerUser;

class User extends Authenticatable
{
    use VoyagerUser;
        ...
}

開啟註冊時 Email 驗證功能

你可以讓使用者註冊好之後,必須要先完成 Email 驗證動作之後才可以進入網頁

開啟這功能會用到 Email 發信,請務必確認 .env 有關 Email 發信的設定是正確的

//.env

MAIL_MAILER=smtp
MAIL_HOST=smtp.gmail.com
MAIL_PORT=587
MAIL_USERNAME=你的EMAIL帳號
MAIL_PASSWORD=你的EMAIL密碼
MAIL_ENCRYPTION=null

Step 1.修改 fortify 設定檔

開啟 config/fortify.php 設定檔,移除 Features::emailVerification() 前面的註解

//config\fortify.php

'features' => [
    ...
    Features::emailVerification(),
    ...
],

Step 2.修改 User 模型

接著需要為用戶模型 User 追加實作 MustVerifyEmail,如下範例:

//app\Models\User.php

namespace App\Models;

class User extends Authenticatable implements MustVerifyEmail

加入登出按鈕

在要登出的地方使用以下程式碼

<form method="POST" action="{{ route('logout') }}">
    @csrf
    <a href="{{ route('logout') }}"
            onclick="event.preventDefault();
                    this.closest('form').submit();">
        <span>登出</span>
    </a>
</form>

保護路由需要登入才能訪問

將要保護的路由放入以下的群組裏頭

//routes\web.php

Route::middleware(['auth'])->group(function () {
    //要驗證才能訪問的路由
    Route::get('/checkout','App\Http\Controllers\SiteController@renderCheckoutPage');
    Route::post('/checkout','App\Http\Controllers\SiteController@checkout');
    Route::get('/shop/cart','App\Http\Controllers\SiteController@renderCartPage');
    Route::get('/shop/addcart/{product}','App\Http\Controllers\SiteController@addCart');
});

分享這篇文章:

關聯文章:

訂閱電子報,索取 Laravel 學習手冊

價值超過 3000 元,包含常用 Laravel 語法與指令!

Laravel 百萬年薪特訓營

從最基礎的 PHP 語法開始,包含所有你該知道的網頁基礎知識,連同 Laravel 從零開始一直到實戰,最後還將告訴你如何找好工作,讓你及早擁有百萬年薪