Một website thường có 2 phần admin dành cho người quản trị và user dành cho người dùng. Để đăng nhập vào 2 phần admin hoặc user của một website ta có thể dùng một table users
hoặc 2 table users
và admins
Dùng 1 table users
trong trường hợp dùng 1 account có thể đăng nhập cả 2 phần admin và user, update password ở user thì ở admin cũng bị update... Dùng 2 table trong trường hợp dùng 2 account khác nhau để đăng nhập admin hoặc user. Thông thường chúng ta sẽ tách biệt account của user và admin nên sẽ dùng 2 bảng.
Để đăng nhập user và admin sử dụng 2 bảng khác nhau chúng ta sẽ sử dụng multi auth với các guard khác nhau. Giờ chúng ta sẽ dùng multi auth với laravel 10 jetstream inertia.
Mặc định laravel đã có bảng users quản lý các user đăng nhập truy cập các chức năng cho user. Giờ chúng ta sẽ tạo bảng admins để quản lý các user đăng nhập truy cập chức năng dành cho admin. Tạo file admin migration bằng command.
php artisan make:migration create_admins_table
<?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
{
Schema::create('admins', function (Blueprint $table) {
$table->id();
$table->string('name');
$table->string('email')->unique();
$table->string('password');
$table->timestamp('email_verified_at')->nullable();
$table->tinyInteger('status')->default(1);
$table->rememberToken();
$table->string('profile_photo_path', 2048)->nullable();
$table->timestamps();
});
}
/**
* Reverse the migrations.
*/
public function down(): void
{
Schema::dropIfExists('admins');
}
};
Tạo admin model tương tự User model. Khai báo thêm admin
guard.
<?php
namespace App\Models;
use Illuminate\Database\Eloquent\Factories\HasFactory;
use Illuminate\Foundation\Auth\User as Authenticatable;
use Illuminate\Notifications\Notifiable;
use Laravel\Jetstream\HasProfilePhoto;
use Laravel\Sanctum\HasApiTokens;
use Laravel\Cashier\Billable;
class Admin extends Authenticatable
{
use HasApiTokens;
use HasFactory;
use HasProfilePhoto;
use Notifiable;
use Billable;
protected $guard = 'admin';
/**
* The attributes that are mass assignable.
*
* @var array<int, string>
*/
protected $fillable = [
'name',
'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',
];
/**
* The accessors to append to the model's array form.
*
* @var array<int, string>
*/
protected $appends = [
'profile_photo_url',
];
}
Tạo các account cho user và admin sử dụng seed. Tạo admin factory
<?php
namespace Database\Factories;
use Illuminate\Database\Eloquent\Factories\Factory;
use App\Models\Admin;
use Illuminate\Support\Str;
/**
* @extends \Illuminate\Database\Eloquent\Factories\Factory<\App\Models\Model>
*/
class AdminFactory extends Factory
{
/**
* The name of the factory's corresponding model.
*
* @var string
*/
protected $model = Admin::class;
/**
* Define the model's default state.
*
* @return array<string, mixed>
*/
public function definition(): array
{
return [
'name' => $this->faker->name(),
'email' => $this->faker->unique()->safeEmail(),
'email_verified_at' => now(),
'password' => '$2y$10$92IXUNpkjO0rOQ5byMi.Ye4oKoEa3Ro9llC/.og/at2.uheWG/igi', // password
'remember_token' => Str::random(10),
'profile_photo_path' => null,
];
}
/**
* Indicate that the model's email address should be unverified.
*/
public function unverified(): static
{
return $this->state(function (array $attributes) {
return [
'email_verified_at' => null,
];
});
}
}
Tạo AdminSeeder và UserSeeder
// AdminSeeder
<?php
namespace Database\Seeders;
use Illuminate\Database\Seeder;
use App\Models\Admin;
use Illuminate\Support\Facades\Hash;
use Illuminate\Database\Console\Seeds\WithoutModelEvents;
class AdminSeeder extends Seeder
{
use WithoutModelEvents;
/**
* Run the database seeds.
*/
public function run(): void
{
if (Admin::get()->count() === 0) {
Admin::factory()->create([
'name' => 'admin',
'email' => 'huu@gmail.com',
'password' => Hash::make('Admin@123'),
'status' => 1,
]);
}
}
}
// User seeder
<?php
namespace Database\Seeders;
use Illuminate\Database\Seeder;
use App\Models\User;
use Illuminate\Support\Facades\Hash;
use Illuminate\Database\Console\Seeds\WithoutModelEvents;
class UserSeeder extends Seeder
{
use WithoutModelEvents;
/**
* Run the database seeds.
*/
public function run(): void
{
if (User::get()->count() === 0) {
User::factory()->create([
'name' => 'user',
'email' => 'huu@gmail.com',
'password' => Hash::make('User@123'),
]);
}
}
}
// Setting seeder
<?php
namespace Database\Seeders;
// use Illuminate\Database\Console\Seeds\WithoutModelEvents;
use Illuminate\Database\Seeder;
class DatabaseSeeder extends Seeder
{
/**
* Seed the application's database.
*/
public function run(): void
{
$this->call([
UserSeeder::class,
AdminSeeder::class,
]);
}
}
Chạy command để tạo bảng admins, users và seed các account demo
// Command create db
php artisan migrate
// Command seed data
php artisan db:seed
Trong laravel authentication sẽ định nghĩa được nhiều guard, mỗi guard tương ứng với một thành phần xác thực khác nhau. định nghĩa các guard trong file config/auth.php
giờ chúng ta sẽ tạo 2 guard là admin và user.
'guards' => [
'web' => [
'driver' => 'session',
'provider' => 'users',
],
'admin' => [
'driver' => 'session',
'provider' => 'admins',
],
],
Thiết lập provider cho các guard.
'providers' => [
'users' => [
'driver' => 'eloquent',
'model' => App\Models\User::class,
],
'admins' => [
'driver' => 'eloquent',
'model' => App\Models\Admin::class,
],
],
Thiết lập reset password
'passwords' => [
'users' => [
'provider' => 'users',
'table' => 'password_resets',
'expire' => 60,
'throttle' => 60,
],
'admins' => [
'provider' => 'admins',
'table' => 'password_resets',
'expire' => 60,
'throttle' => 60,
],
],
Thêm admin route routes/admin.php
bằng cách khai báo trong App\Providers\RouteServiceProvider
$this->routes(function () {
Route::middleware('api')
->prefix('api')
->group(base_path('routes/api.php'));
Route::middleware('web')
->group(base_path('routes/web.php'));
Route::middleware('web')
->prefix('admin')
->group(base_path('routes/admin.php'));
});
Khai báo route cho user
Route::middleware(['guest'])->group(function () {
Route::get('/login', [LoginController::class, 'index'])->name('user.login.index');
Route::post('/login', [LoginController::class, 'store'])->name('user.login.store');
});
Route::group(['middleware' => ['auth']], function () {
Route::get('/home', [HomeController::class, 'index'])->name('user.home.index');
Route::get('/logout', [LoginController::class, 'logout'])->name('user.logout');
});
Khai báo route cho admin
<?php
use Illuminate\Foundation\Application;
use Illuminate\Support\Facades\Route;
use App\Http\Controllers\Admin\LoginController;
use App\Http\Controllers\Admin\HomeController;
/*
|--------------------------------------------------------------------------
| Web Routes
|--------------------------------------------------------------------------
|
| Here is where you can register web routes for your application. These
| routes are loaded by the RouteServiceProvider within a group which
| contains the "web" middleware group. Now create something great!
|
*/
Route::name('admin.')->namespace('Admin')->group(function () {
Route::middleware(['guest:admin'])->group(function () {
Route::get('/login', [LoginController::class, 'index'])->name('login.index');
Route::post('/login', [LoginController::class, 'store'])->name('login.store');
});
Route::group(['middleware' => ['auth:admin']], function () {
Route::get('/home', [HomeController::class, 'index'])->name('home.index');
Route::get('/logout', [LoginController::class, 'logout'])->name('logout');
});
});
Middleware auth:admin
các trang yêu cầu auth với guard admin
là người dùng thuộc bảng admins. Middleware auth là guard mặc định tương ứng với auth:web
các trang thuộc middleware này cần auth với bảng users.
Middleware guest
người dùng không cần auth có thể truy cập vào các trang này, người dùng đã đăng nhập truy cập vào các trang thuộc guest tự điều hướng về trang home.
<?php
namespace App\Http\Middleware;
use App\Providers\RouteServiceProvider;
use Closure;
use Illuminate\Http\Request;
use Illuminate\Support\Facades\Auth;
use Symfony\Component\HttpFoundation\Response;
class RedirectIfAuthenticated
{
/**
* Handle an incoming request.
*
* @param \Closure(\Illuminate\Http\Request): (\Symfony\Component\HttpFoundation\Response) $next
*/
public function handle(Request $request, Closure $next, string ...$guards): Response
{
$guards = empty($guards) ? [null] : $guards;
foreach ($guards as $guard) {
if ($guard == "admin" && Auth::guard($guard)->check()) {
return redirect(RouteServiceProvider::ADMIN_HOME);
}
if (Auth::guard($guard)->check()) {
return redirect(RouteServiceProvider::HOME);
}
}
return $next($request);
}
}
Middleware auth
chỉ user đăng nhập mới vào được các trang này, nếu truy cập vào trang mà không có auth thì tự redirect ra trang login.
<?php
namespace App\Http\Middleware;
use Illuminate\Auth\Middleware\Authenticate as Middleware;
use Illuminate\Http\Request;
class Authenticate extends Middleware
{
/**
* Get the path the user should be redirected to when they are not authenticated.
*/
protected function redirectTo(Request $request): ?string
{
if ($request->expectsJson()) {
return null;
}
if ($request->is('admin') || $request->is('admin/*')) {
return route('admin.login.index');
}
return route('user.login.index');
}
}
Tạo controller cho trang login, logout của user.
<?php
namespace App\Http\Controllers;
use Inertia\Inertia;
use Illuminate\Support\Facades\Auth;
use App\Http\Requests\AuthRequest;
class LoginController extends Controller
{
public function index()
{
return Inertia::render('Auth/Login');
}
public function store(AuthRequest $request)
{
$auth = Auth::attempt([
'email' => $request->email,
'password' => $request->password,
],
$request->get('remember')
);
if (!$auth) {
return back()->withErrors([
'email' => 'The provided credentials do not match our records.',
])->onlyInput('email');
}
return redirect()->route('user.home.index');
}
public function logout()
{
Auth::logout();
return redirect('/login');
}
}
Tạo AuthRequest
validate input khi người dùng đăng nhập.
<?php
namespace App\Http\Requests;
use Illuminate\Foundation\Http\FormRequest;
class AuthRequest extends FormRequest
{
/**
* Determine if the user is authorized to make this request.
*/
public function authorize(): bool
{
return true;
}
/**
* Get the validation rules that apply to the request.
*
* @return array<string, \Illuminate\Contracts\Validation\ValidationRule|array|string>
*/
public function rules(): array
{
return [
'email' => 'required|email',
'password' => 'required|string|min:6',
];
}
}
Tạo controller cho trang home của user, trang yêu cầu người dùng đăng nhập với thông tin của bảng user mới có thể truy cập được.
<?php
namespace App\Http\Controllers\Admin;
use App\Http\Controllers\Controller;
use Inertia\Inertia;
use Illuminate\Support\Facades\Auth;
class HomeController extends Controller
{
public function index()
{
$admin = Auth::guard('admin')->user();
return Inertia::render('Admin/Welcome', ['admin' => $admin]);
}
}
Sử dụng các view mặc định khi cài đặt laravel jetstream inertia trong thư mục resources/js/Pages
View cho trang login cho user trong file resources/js/Pages/Auth/Login.vue
<script setup>
import { Head, Link, useForm } from '@inertiajs/vue3';
import AuthenticationCard from '@/Components/AuthenticationCard.vue';
import AuthenticationCardLogo from '@/Components/AuthenticationCardLogo.vue';
import Checkbox from '@/Components/Checkbox.vue';
import InputError from '@/Components/InputError.vue';
import InputLabel from '@/Components/InputLabel.vue';
import PrimaryButton from '@/Components/PrimaryButton.vue';
import TextInput from '@/Components/TextInput.vue';
defineProps({
canResetPassword: Boolean,
status: String,
});
const form = useForm({
email: '',
password: '',
remember: false,
});
const submit = () => {
form.transform(data => ({
...data,
remember: form.remember ? 'on' : '',
})).post(route('user.login.store'), {
onFinish: () => form.reset('password'),
});
};
</script>
<template>
<Head title="Log in" />
<AuthenticationCard>
<template #logo>
<AuthenticationCardLogo />
</template>
<div v-if="status" class="mb-4 font-medium text-sm text-green-600">
{{ status }}
</div>
<form @submit.prevent="submit">
<div>
<InputLabel for="email" value="Email" />
<TextInput
id="email"
v-model="form.email"
type="email"
class="mt-1 block w-full"
required
autofocus
autocomplete="username"
/>
<InputError class="mt-2" :message="form.errors.email" />
</div>
<div class="mt-4">
<InputLabel for="password" value="Password" />
<TextInput
id="password"
v-model="form.password"
type="password"
class="mt-1 block w-full"
required
autocomplete="current-password"
/>
<InputError class="mt-2" :message="form.errors.password" />
</div>
<div class="block mt-4">
<label class="flex items-center">
<Checkbox v-model:checked="form.remember" 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">
<Link v-if="canResetPassword" :href="route('password.request')" class="underline text-sm text-gray-600 hover:text-gray-900 rounded-md focus:outline-none focus:ring-2 focus:ring-offset-2 focus:ring-indigo-500">
Forgot your password?
</Link>
<PrimaryButton class="ml-4" :class="{ 'opacity-25': form.processing }" :disabled="form.processing">
Log in
</PrimaryButton>
</div>
</form>
</AuthenticationCard>
</template>
Tạo controller cho trang login, logout admin
<?php
namespace App\Http\Controllers\Admin;
use App\Http\Controllers\Controller;
use Illuminate\Support\Facades\Auth;
use Inertia\Inertia;
use App\Http\Requests\AuthRequest;
use Illuminate\Http\Request;
class LoginController extends Controller
{
public function index()
{
return Inertia::render('Admin/Auth/Login');
}
public function store(AuthRequest $request)
{
$auth = Auth::guard('admin')
->attempt([
'email' => $request->email,
'password' => $request->password,
],
$request->get('remember')
);
if (!$auth) {
return back()->withErrors([
'email' => 'The provided credentials do not match our records.',
])->onlyInput('email');
}
return redirect()->route('admin.home.index');
}
public function logout(Request $request)
{
Auth::guard('admin')->logout();
$request->session()->invalidate();
$request->session()->regenerateToken();
return redirect('/admin/login');
}
}
Tạo controller cho trang home của admin
<?php
namespace App\Http\Controllers\Admin;
use App\Http\Controllers\Controller;
use Inertia\Inertia;
use Illuminate\Support\Facades\Auth;
class HomeController extends Controller
{
public function index()
{
$admin = Auth::guard('admin')->user();
return Inertia::render('Admin/Welcome', ['admin' => $admin]);
}
}
Các view của admin tạo tương tự như view của user.
Khi làm hết các bước trên là chúng ta đã có thể sử dụng multi auth rồi, giờ chỉ việc chạy ứng dụng và login vào trang user hoặc admin thôi. Để chạy ứng dụng có thể dùng docker, hoặc command sau
php artisan serve
Đăng nhập vào các trang dành cho user qua link http://localhost:8000/login
. Sau khi đăng nhập xong người dùng sẽ được chuyển hướng về trang http://localhost:8000/home
. Những người dùng chưa đăng nhập truy cập vào trang home
sẽ chuyển hướng về trang login.
Đăng nhập vào các trang dành cho admin qua link http://localhost:8000/admin/login
. Khi user đăng nhập xong thì sẽ được chuyển hướng về trang http://localhost:8000/admin/home
. User chưa đăng nhập sẽ chuyển về trang login của admin
Sử dụng auth multi với guard cũng có thể phân quyền theo từng guard khi sử dụng laravel permission giúp chúng ta có thể phân quyên cho 2 site user và admin độc lập
Như vậy là chúng ta đã sử dụng được multi auth với laravel 10 sử dụng laravel jetstream inertia, sử dụng view blade thông thường cũng làm tương tự như trên chỉ khác nhau ở view. Thanks for reading...