Laravel 提供了一個功能全面的認證系統,用於處理用戶註冊、登錄和密碼重置等功能。
它內建了一些實用的功能和工具,使得用戶認證的實現變得更簡單和高效。
主要特點:
Auth
在 Laravel 8.x 及以後版本中,make:auth
命令不再直接支持。因此,需要安裝 Laravel Breeze 或 Laravel Jetstream 來添加認證系統。
如果不想安裝套件也可以使用 Laravel 的 Auth
facade ,他提供了一個簡單的接口來訪問身份驗證服務,允許開發進行用戶認證、登錄、登出、檢查當前用戶等操作。
以下是如何使用 Auth facade 來管理和存取身份驗證服務的詳細說明:
<?php
namespace App\Http\Controllers;
use Illuminate\Http\Request;
use Illuminate\Support\Facades\Auth;
class LoginController extends Controller
{
/**
* 處理身份驗證嘗試
*
* @param \Illuminate\Http\Request $request
* @return \Illuminate\Http\Response
*/
public function authenticate(Request $request)
{
// 驗證用戶提交的數據
$credentials = $request->validate([
'email' => ['required', 'email'],
'password' => ['required'],
]);
/**
* 用提供的憑證(email 和 password)去資料庫比對
* 通過後會重新生成用戶的會話 ID,以防止會話劫持
*/
if (Auth::attempt($credentials)) {
$request->session()->regenerate();
return redirect()->intended('dashboard');
}
// 如果身份驗證失敗,畫面會出現錯誤訊息
return back()->withErrors([
'email' => 'The provided credentials do not match our records.',
]);
}
}
使用 Auth
Facade 的基本操作
Auth::check()
:如果用戶已經登錄,這個方法會返回 true,否則返回 false。
use Illuminate\Support\Facades\Auth;
if (Auth::check()) {
// 用戶已經登錄
$user = Auth::user(); // 獲取當前登錄的用戶
} else {
// 用戶尚未登錄
}
Auth::user()
:這個方法返回一個 User 模型實例。
use Illuminate\Support\Facades\Auth;
$user = Auth::user();
if ($user) {
// 輸出用戶名稱
echo $user->name;
}
Auth::attempt()
:接受一個包含用戶憑證的陣列(例如,電子郵件和密碼),並返回布林值表示是否成功登錄。
use Illuminate\Support\Facades\Auth;
$credentials = [
'email' => 'user@example.com',
'password' => 'password123'
];
if (Auth::attempt($credentials)) {
// 登錄成功
return redirect()->intended('dashboard');
} else {
// 登錄失敗
return redirect()->back()->withErrors([
'email' => 'The provided credentials do not match our records.',
]);
}
Auth::logout()
:登出當前登錄的用戶。
use Illuminate\Support\Facades\Auth;
Auth::logout();
// 重定向到首頁
return redirect('/');
Auth::login()
:手動登錄用戶,通常在測試環境中使用。
use Illuminate\Support\Facades\Auth;
use App\Models\User;
$user = User::find(1); // 找到用戶
Auth::login($user); // 登錄用戶
Auth::guard()
:Laravel 支持多種認證守衛(guards),可以在 config/auth.php 文件中配置。
use Illuminate\Support\Facades\Auth;
$admin = Auth::guard('admin')->user();
use Illuminate\Support\Facades\Auth;
if (Auth::check() && Auth::user()->hasRole('admin')) {
// 用戶是管理員
}
執行數據遷移
安裝認證系統後,運行遷移命令 php artisan migrate
來創建必要的數據表
參考資料:Laravel 9.x 驗證
Step 1:建立資料庫連線,設定 .env
檔案
安裝 Laravel 並配置好資料庫連線,通常是在 .env
檔案中設定
DB_CONNECTION=sqlite //使用的資料庫,範例中是使用mysql
DB_HOST=127.0.0.1 //資料庫若是再別的主機再另行設定
DB_PORT=3306 //資料庫若是再別的主機再另行設定
DB_DATABASE='' //資料庫名稱
🔔 若是使用
php artisan serve
開發,修改.env
後必須重開php artisan serve
Step 2:建立認證相關的表
下指令 php artisan make:migration register_list
後,會新增一個檔案 database\migrations\2024_08_14_143812_register_list.php
內
<?php
use Illuminate\Database\Migrations\Migration;
use Illuminate\Database\Schema\Blueprint;
use Illuminate\Support\Facades\Schema;
return new class extends Migration
{
/**
* Run the migrations.
*
* @return void
*/
public function up()
{
// 建立新的名為 register_list 的 table
Schema::create('register_list', function (Blueprint $table) {
// 建立 column 主鍵(primary key),並且為自動遞增
$table->id();
// 建立名為 email 的 column,只能是唯一
$table->string('email')->unique();
// 建立名為 email_verified_at 的 column,並且允許為空值
$table->timestamp('email_verified_at')->nullable();
// 建立名為 password 的column,type 為字串
$table->string('password');
// 建立名為 remember_token 記住我的 column
$table->rememberToken();
// 建立 create_at、update_at 的 column,type 為date。create_at 會在新增時自動紀錄時間,update_at 會再更新時自動紀錄時間
$table->timestamps();
});
}
/**
* Reverse the migrations.
*
* @return void
*/
public function down()
{
//
}
};
Step 3:建立 table
下指令 php artisan migrate
laravel 就會自動將 table 建立好
Step 4:建立 model
MVC 結構中的 "M",也就是 model,基本上 model 會對應到資料庫,每個 model 對應一個 table,下指令 php artisan make:model Users
,就會產生一個名為 Users 空的模型,再放上資料庫中的 table 名稱,還設定的 column
<?php
namespace App\Models;
use Illuminate\Database\Eloquent\Factories\HasFactory;
use Illuminate\Database\Eloquent\Model;
class Users extends Model
{
use HasFactory;
protected $table = 'users'; //DB 中的 table 名稱
public $primaryKey = 'id';
protected $fillable = ['username', 'password', 'email', 'remember_token']; //白名單,就是可以修改的欄位
protected $hidden = [
'password', 'remember_token',
]; //可以隱藏的欄位
}
Step 5:設定註冊和登入控制器,並且使用 model
用戶可以通過提供必要的資料(如電子郵件和密碼)來註冊新帳戶。
use App\Models\Users;
use Illuminate\Support\Facades\Hash;
use Illuminate\Http\Request;
class RegisterController extends Controller
{
public function register(Request $request)
{
// 驗證請求
$validatedData = $request->validate([
'email' => ['required', 'email|unique:users,email'],
'password' => ['required, min:8', 'confirmed'],
]);
// 建立使用者
$user = Users::create([
'email' => $validatedData['email'],
'password' => Hash::make($validatedData['password']),
]);
// 你可以在這裡選擇傳送驗證郵件
return redirect()->route('login')->with('success', '註冊成功!請登入');
}
}
用戶可以通過電子郵件和密碼登錄。
use Illuminate\Support\Facades\Auth;
class LoginController extends Controller
{
public function login(Request $request)
{
// 驗證請求
$validatedData = $request->validate([
'email' => 'required|email',
'password' => 'required',
]);
// 嘗試登入
if (Auth::attempt(['email' => $validatedData['email'], 'password' => $validatedData['password']])) {
return redirect()->intended('home')->with('success', '登入成功!');
}
return redirect()->back()->withErrors(['email' => '電子信箱或密碼不正確']);
}
}
Step 6:建立 view.bland.php
<form method="POST" action="{{ route('register') }}">
@csrf
<label for="email">電子信箱:</label>
<input type="email" name="email" required>
<label for="password">密碼:</label>
<input type="password" name="password" required>
<label for="password_confirmation">確認密碼:</label>
<input type="password" name="password_confirmation" required>
<button type="submit">註冊</button>
</form>
Step 7:設定註冊路由routes/web.php
會包含對應的路由,app/Http/Controllers/Auth/RegisterController.php
處理註冊邏輯。
use App\Http\Controllers\Auth\RegisterController;
use App\Http\Controllers\Auth\LoginController;
Route::get('register', function () {
return view('register');
})->name('register');
Route::post('register', [RegisterController::class, 'register']);
Route::get('login', function () {
return view('login');
})->name('login');
Route::post('login', [LoginController::class, 'login']);
用 laravel 內建的認證系統 auth,嘗試驗證使用者提供給前端呼叫 api 的資料
建立連線資料庫
php artisan make:model RegisterUsers
和資料表 php artisan make:migration create_register_users_table
2024_08_19_135051_create_register_users_table.php
中,添加基本的用戶欄位
<?php
use Illuminate\Database\Migrations\Migration;
use Illuminate\Database\Schema\Blueprint;
use Illuminate\Support\Facades\Schema;
return new class extends Migration {
/**
* Run the migrations.
*/
public function up(): void
{
// 建立新的名為 register_list 的 table
Schema::create('register_users', function (Blueprint $table) {
// 建立 column 主鍵(primary key),並且為自動遞增
$table->id();
// 建立名為 email 的 column,只能是唯一
$table->string('email')->unique();
// 建立名為 email_verified_at 的 column,並且允許為空值
$table->timestamp('email_verified_at')->nullable();
// 建立名為 password 的column,type 為字串
$table->string('password');
// 建立名為 remember_token 記住我的 column
$table->rememberToken();
// 建立 create_at、update_at 的 column,type 為date。create_at 會在新增時自動紀錄時間,update_at 會再更新時自動紀錄時間
$table->timestamps();
});
}
/**
* Reverse the migrations.
*/
public function down(): void
{
//
}
};
php artisan migrate
建立 API 路由web.php
和 api.php
本身有差異,所以 api 放哪裡變成很重要,練習過程我發現兩邊要顧到真的太難還會弄錯,所以最後決定在 web.php
練習
🐘 補充說明:
如果今天要跟一個只會 JavaScript 的後端新手解釋web.php
和api.php
兩個差異,我覺得這樣比較好懂:web.php
-> 在瀏覽器上可以看到的內容,通常只能使用 GET 和 POSTapi.php
-> 在 postman 打才能看到的內容,所有的 Restful API 都可以使用
<?php
use App\Http\Controllers\AuthController;
use Illuminate\Support\Facades\Route;
// 註冊
Route::get('/', [AuthController::class, 'register'])->name('register');
Route::post('/register', [AuthController::class, 'handelRegister']);
// 登入
Route::get('login', [AuthController::class, 'login'])->name('login');
Route::post('login', [AuthController::class, 'handelLogin']);
// 首頁
Route::get('/home', [AuthController::class, 'index'])->name('home');
// 登出
Route::post('/logout', [AuthController::class, 'logout'])->name('logout');
建立控制器驗證 API 請求
php artisan make:controller AuthController
<?php
namespace App\Http\Controllers;
use App\Models\RegisterUsers;
use Illuminate\Contracts\View\View;
use Illuminate\Http\JsonResponse;
use Illuminate\Http\RedirectResponse;
use Illuminate\Http\Request;
use Illuminate\Routing\Redirector;
use Illuminate\Support\Facades\Auth;
use Illuminate\Support\Facades\Hash;
use MongoDB\Driver\Session;
class AuthController extends Controller
{
/**
* 進入註冊畫面
*
* @return View
*/
public function register(): View
{
return view('register');
}
/**
* 建立使用者資訊到資料庫
*
* @param Request $request
* @return string
*/
public function handelRegister(Request $request): string
{
$validatedData = $request->validate([
'email' => 'required|email|unique:register_users|max:70',
'password' => 'required|min:2|max:16'
]);
// 建立使用者
$user = RegisterUsers::query()->create([
'email' => $validatedData['email'],
'password' => Hash::make($validatedData['password'])
]);
return redirect()->route('login')->with('success', '註冊成功!請登入');
}
/**
* 進入登入畫面
*
* @return View
*/
public function login(): View
{
return view('login');
}
/**
* 處理登入驗證(規劃 - 成功跳轉頁面,失敗轉回登入頁面)
*
* @param Request $request
* @return string
*/
public function handelLogin(Request $request): string
{
try {
$validatedData = $request->validate([
'email' => 'required|email','password' => 'required'
]);
} catch (\Throwable $exception) {
damp($exception->getMessage());
$validatedData = ['email' => 'aaa@test.net', 'password' => 'xxx'];
}
if (Auth::attempt($validatedData, true)) {
$request->session()->regenerate();
return redirect()->intended('home');
}
return redirect()->route('login')->withErrors(['email' => '電子信箱或密碼不正確']);
}
/**
* 進入首頁畫面
*
* @return View
*/
public function index(): View
{
return view('welcome');
}
/**
* 登出
*
* @param Request $request
* @return RedirectResponse
*/
public function logout(Request $request): RedirectResponse
{
Auth::logout();
$request->session()->invalidate();
$request->session()->regenerateToken();
return redirect()->route('login')->withErrors(['logout' => '你已經登出'])->withInput();
}
🐘 補充說明:(參考內容)
在 Laravel 的核心中,身份驗證工具是由「守衛」和「提供者」所組成。
守衛定義了在每個請求中(file path:config/auth.php
),如何與使用者進行身份驗證。
舉例來說,Laravel 內建的一個 session 守衛會使用 session 儲存器和 cookies 來維護驗證狀態。然後另一個 token 守衛會使用每個請求所傳遞的「API token」來認證使用者。
建立 blade.php
註冊頁面
<!doctype html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport"
content="width=device-width, user-scalable=no, initial-scale=1.0, maximum-scale=1.0, minimum-scale=1.0">
<meta http-equiv="X-UA-Compatible" content="ie=edge">
<title>register</title>
<link rel="stylesheet" href="https://stackpath.bootstrapcdn.com/bootstrap/4.3.1/css/bootstrap.min.css">
<script src="https://maxcdn.bootstrapcdn.com/bootstrap/3.3.7/js/bootstrap.min.js"> </script>
<style>
label {
font-weight: 600;
color: #666;
}
.box8{
box-shadow: 0px 0px 5px 1px #999;
}
.mx-t3{
margin-top: -3rem;
}
</style>
</head>
<body>
<div class="container mt-3">
<form method="post" action="/register">
@csrf
<div class="row jumbotron box8">
<div class="col-sm-12 mx-t3 mb-4">
<h2 class="text-center text-info">Register</h2>
</div>
<div class="col-sm-12 form-group">
<label for="email">Email</label>
<input type="email" class="form-control" name="email" id="email" placeholder="Enter your email."
required>
</div>
<div class="col-sm-12 form-group">
<label for="pass">Password</label>
<input type="Password" name="password" class="form-control" id="pass" placeholder="Enter your password."
required>
</div>
<div class="col-sm-12 form-group">
<label for="pass2">Confirm Password</label>
<input type="Password" name="cnf-password" class="form-control" id="pass2"
placeholder="Re-enter your password." required>
</div>
<div class="col-sm-12 form-group mb-0">
<button class="btn btn-primary float-right">Submit</button>
</div>
</div>
</form>
</div>
</body>
</html>
登入畫面
<!doctype html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport"
content="width=device-width, user-scalable=no, initial-scale=1.0, maximum-scale=1.0, minimum-scale=1.0">
<meta http-equiv="X-UA-Compatible" content="ie=edge">
<title>Login</title>
<link href="//netdna.bootstrapcdn.com/twitter-bootstrap/2.3.2/css/bootstrap-combined.min.css" rel="stylesheet">
<style>
#login img {
margin: 10px 0;
}
#login .center {
text-align: center;
}
#login .login {
max-width: 300px;
margin: 35px auto;
}
#login .login-form {
padding: 0px 25px;
}
</style>
</head>
<body>
<div id="login" class="container">
<div class="row-fluid">
<div class="span12">
<div class="login well well-small">
<div class="center">
<img src="http://placehold.it/250x100&text=Logo" alt="logo">
</div>
@if ($errors->any())
<div class="alert alert-danger">
<ul style="margin-bottom: 0">
@foreach ($errors->all() as $error)
<li>{{ $error }}</li>
@endforeach
</ul>
</div>
@endif
<form method="POST" action="/login" style="" class="login-form" id="UserLoginForm"
accept-charset="utf-8">
@csrf
<div class="control-group">
<div class="input-prepend">
<span class="add-on"><i class="icon-user"></i></span>
<input type="email" class="form-control" name="email" id="email" placeholder="Enter your email."
required>
</div>
</div>
<div class="control-group">
<div class="input-prepend">
<span class="add-on"><i class="icon-lock"></i></span>
<input type="Password" name="password" class="form-control" id="pass" placeholder="Enter your password."
required>
</div>
</div>
<div class="control-group">
<label id="remember-me" style="display: flex; align-items: start" for="remember">
<input type="checkbox" style="margin-right: 10px;" name="remember" value="1" id="UserRememberMe">
Remember Me?</label>
</div>
<div class="control-group">
<input class="btn btn-primary btn-large btn-block" type="submit" value="Sign in">
</div>
</form>
</div>
</div>
</div>
</div>
</body>
</html>
登出畫面按鈕
@auth
<form action="/logout" method="post">
@csrf
<button type="submit"> Log Out </button>
</form>
@else