本篇純粹紀錄實作流程,以方便未來可以照此版繼續優化改進作法,這篇會使用Laravel 9版本並搭配Boostrap 5來做開發。
發現將每一次的實作作筆記,回頭再看的時候,就可以發現實作差異並進行改正,沒搞懂的底層操作也能在覆盤的時候理解,建議大家都要筆記自己的實作流程。
我們將用vite編譯工具來幫我們產生JS and CSS,詳細配置可以看一下先前的文章。 實作 Laravel 9 改用 vite 作為前端編譯檔案配置
首先先設計註冊畫面,創建一個命名為signUp.blade.php
,附上範例code
@extends('layouts.app')
@section('content')
<div class="row d-flex justify-content-center align-items-center h-100 vh-100">
<div class="col-12 col-md-8 col-lg-6 col-xl-5">
<div class="card shadow-2-strong" style="border-radius: 1rem;">
<div class="card-body p-5">
<form method="POST" action="{{ route('register') }}">
@csrf
<h3 class="mb-5 text-center">會員註冊</h3>
<div class="form-outline mb-4">
<label for="account" class="form-label">會員帳號</label>
<input
id="account"
type="text"
class="form-control form-control-lg"
placeholder="Account"
aria-label="Account"
aria-describedby="basic-addon1"
name="account"
/>
</div>
<div class="form-outline mb-4">
<label for="userName" class="form-label">會員名稱</label>
<input
id="userName"
type="text"
class="form-control form-control-lg"
placeholder="UserName"
aria-label="userName"
aria-describedby="basic-addon1"
name="userName"
/>
</div>
<div class="form-outline mb-4">
<label for="password" class="form-label">會員密碼</label>
<input
id="password"
type="password"
id="password"
class="form-control form-control-lg"
placeholder="Password"
aria-label="Password"
aria-describedby="basic-addon1"
name="password"
/>
</div>
<div class="form-outline mb-4">
<label for="password-confirm" class="form-label">確認密碼</label>
<input
id="password-confirm"
type="password"
class="form-control form-control-lg"
name="password_confirmation"
required
autocomplete="new-password"
placeholder="PasswordConfirm"
/>
</div>
<!-- button -->
<div class="d-grid gap-2">
<button class="btn btn-primary btn-lg" type="submit">
REGISTER
</button>
</div>
</form>
</div>
</div>
</div>
</div>
@endsection
畫面長這樣(很簡陋的東西XDDDD)
接著撰寫Controller邏輯
php artisan make:controller AuthController
編寫取得signUp.blade.php
View跟註冊 register邏輯方法
註:這邊function
命名方式採用camelCase
,駝峰命名方式,又稱小駝峰命名方式(lowerCamelCase)
<?php
namespace App\Http\Controllers;
use Exception;
use App\Models\User;
use Illuminate\Support\Str;
use App\Http\Requests\LoginRequest;
use Illuminate\Support\Facades\Hash;
use App\Http\Requests\RegisterRequest;
class AuthController extends Controller
{
public function signUp()
{
return view('signUp');
}
public function register(RegisterRequest $request)
{
$uid = Str::uuid();
$account = $request->request->get('account');
$userName = $request->request->get('userName');
$password = $request->request->get('password');
User::create([
'uid' => $uid,
'account' => $account,
'userName' => $userName,
'password' => bcrypt($password),
]);
return redirect()->route('signIn');
}
}
這邊的Request改採用Form Request來編寫,方便以後寫特定的Rules。
php artisan make:request RegisterRequest
再來修改原本的migration table User Table的部分,修改部分為綠色code的樣子,主要是我的id
想要用成uid
的方式,email
改為允許為null
,主鍵改成uid
,配置完成執行migrate
php artisan migrate
接著配置User Model
<?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\Sanctum\HasApiTokens;
class User extends Authenticatable
{
use HasApiTokens, HasFactory, Notifiable;
protected $primaryKey = 'uid';
protected $keyType = 'string';
protected $guarded = [];
/**
* The attributes that are mass assignable.
*
* @var array<int, string>
*/
protected $fillable = [
'uid',
'userName',
'account',
'email',
'password',
];
/**
* The attributes that should be hidden for serialization.
*
* @var array<int, string>
*/
protected $hidden = [
'password',
'remember_token',
];
/**
* The attributes that should be cast.
*
* @var array<string, string>
*/
protected $casts = [
'email_verified_at' => 'datetime',
];
}
配置路由web.php
檔案,而路由檔支持的寫法有很多種,詳下圖
接下來確認新增都沒問題就大功完成註冊的部分~
會員登入的畫面拿註冊畫面來改,一樣先創建signIn.blade.php
畫面
@extends('layouts.app')
@section('content')
<div class="row d-flex justify-content-center align-items-center h-100 vh-100">
<div class="col-12 col-md-8 col-lg-6 col-xl-5">
<div class="card shadow-2-strong" style="border-radius: 1rem;">
<div class="card-body p-5">
<form action="{{ route('login') }}" method="post" enctype="multipart/form-data">
@csrf
<h3 class="mb-5 text-center">LOGIN</h3>
<div class="form-outline mb-4">
<label for="account" class="form-label">會員帳號</label>
<input id="account" type="text" class="form-control form-control-lg" placeholder="Account"
aria-label="Account" aria-describedby="basic-addon1" name="account">
</div>
<div class="form-outline mb-4">
<label for="password" class="form-label">會員密碼</label>
<input id="password" type="password" id="password" class="form-control form-control-lg"
placeholder="Password" aria-label="Password" aria-describedby="basic-addon1" name="password">
</div>
<!-- Checkbox -->
{{-- <div class="form-check d-flex justify-content-start mb-4">
<input class="form-check-input" type="checkbox" value="" id="form1Example3" />
<label class="form-check-label" for="form1Example3"> Remember password </label>
</div> --}}
<!-- button -->
<div class="d-grid gap-2">
<a type="submit" id="create_form" class="btn btn-primary btn-lg">Login</a>
{{-- <button class="btn btn-primary btn-lg" type="submit" id="create_form">Login</button> --}}
<hr class="my-2">
<button class="btn btn-lg btn-block btn-primary" style="background-color: #dd4b39;"
type="submit"><i class="fab fa-google me-2"></i> Sign in with google</button>
<hr class="my-2">
<button class="btn btn-lg btn-block btn-primary mb-2" style="background-color: #3b5998;"
type="submit"><i class="fab fa-facebook-f me-2"></i>Sign in with facebook</button>
</div>
</form>
</div>
</div>
</div>
</div>
@endsection
這邊使用tymon/jwt-auth
套件,並根據使用文檔配置,來設定我們的Private Key
,並搭配 firebase/php-jwt
,來創建我們的Token
composer require tymon/jwt-auth
配置vendor,接著會產生config/jwt.php
檔案,有些設定可以在這邊改
php artisan vendor:publish --provider="Tymon\JWTAuth\Providers\LaravelServiceProvider"
產生金鑰
php artisan jwt:secret
安裝firebase/php-jwt
composer require firebase/php-jwt
接著開始寫產生Token的程式邏輯,這邊創建Services/AuthService.php
檔案,並定義一個encode
方法
接著修改AuthController
檔案,增加login
方法,驗證登入帳密及產生Token
,並由Service端把token塞到cookie,方便前端取用。
Token在前端保存有多種方式,額外再寫一篇補充說明,看完一些文件說明,大致上沒有一定需要怎麼做🧐
<?php
namespace App\Http\Controllers;
use Exception;
use App\Models\User;
use Illuminate\Support\Str;
use App\Services\AuthService;
use App\Http\Requests\LoginRequest;
use Illuminate\Support\Facades\Hash;
use App\Http\Requests\RegisterRequest;
class AuthController extends Controller
{
private AuthService $authService;
public function __construct(
AuthService $authService
) {
$this->authService = $authService;
}
public function signUp()
{
return view('signUp');
}
public function signIn()
{
return view('signIn');
}
public function register(RegisterRequest $request)
{
$uid = Str::uuid();
$account = $request->request->get('account');
$userName = $request->request->get('userName');
$password = $request->request->get('password');
User::create([
'uid' => $uid,
'account' => $account,
'userName' => $userName,
'password' => bcrypt($password),
]);
return redirect()->route('signIn');
}
public function login(LoginRequest $request)
{
$account = $request->request->get('account');
$password = $request->request->get('password');
$userData = User::where('account', $account)->first();
if (!$userData || !Hash::check($password, $userData->password)) {
throw new Exception('密碼錯誤');
}
$token = $this->authService->encode($userData);
$minutes = 5;
return response()->json([
'status' => 'success',
'data' => [
'access_token' => $token,
'token_type' => 'bearer',
'expired_at' => now()->addMinutes(config('jwt.ttl'))->timestamp
]
])->withCookie(cookie('token', $token, $minutes));
}
}
新增login.js檔案,並使用AJAX處理,在登入成功確認response的結果是否有Token。
接著測試登入結果有正常取得Token結果