Laravel 8 中多角色多区段认证与重定向策略优化
在Laravel 8开发多角色系统时,我们常遇到不同角色(如管理员、普通用户、商家)需要登录不同路由前缀(如/admin、/user、/merchant)的场景,同时还需要根据角色类型自动跳转到对应后台,避免手动选择登录入口的繁琐。本文将介绍如何通过自定义认证守卫、路由前缀和中间件,实现多角色多区段认证与智能重定向。
核心需求分析
我们的目标系统需要满足以下要求:
- 不同角色使用独立的登录入口,对应不同的路由前缀:管理员走/admin/login,普通用户走/user/login,商家走/merchant/login
- 每个角色登录后自动跳转到对应前缀的后台页面,无需手动切换
- 访问未授权的前缀路由时,自动重定向到对应角色的登录页
- 支持同一浏览器同时登录不同角色(可选,基于会话隔离)
第一步:配置多守卫与用户模型
首先在config/auth.php中定义多个认证守卫,每个角色对应一个独立的守卫,使用不同的用户模型和会话驱动,避免会话冲突。同时需要转义提到的配置项中的HTML特殊字符,比如配置中的<model>标识无需转义但属于配置内容,直接按原格式书写即可。
// config/auth.php 守卫配置部分
'guards' => [
'web' => [
'driver' => 'session',
'provider' => 'users',
],
'admin' => [
'driver' => 'session',
'provider' => 'admins',
],
'merchant' => [
'driver' => 'session',
'provider' => 'merchants',
],
],
// 用户提供者配置
'providers' => [
'users' => [
'driver' => 'eloquent',
'model' => App\Models\User::class,
],
'admins' => [
'driver' => 'eloquent',
'model' => App\Models\Admin::class,
],
'merchants' => [
'driver' => 'eloquent',
'model' => App\Models\Merchant::class,
],
],接下来创建对应的用户模型,每个模型需要指定对应的表名,并实现Authenticatable接口。以Admin模型为例:
<?php
namespace App\Models;
use Illuminate\Foundation\Auth\User as Authenticatable;
use Illuminate\Database\Eloquent\Factories\HasFactory;
class Admin extends Authenticatable
{
use HasFactory;
protected $table = 'admins'; // 管理员表名
protected $fillable = ['name', 'email', 'password'];
protected $hidden = ['password', 'remember_token'];
}第二步:创建带前缀的登录路由与控制器
在routes/web.php中定义不同角色的前缀路由组,每个路由组使用独立的中间件和命名空间,避免路由冲突。
<?php
use Illuminate\Support\Facades\Route;
// 管理员路由组
Route::prefix('admin')->name('admin.')->group(function () {
Route::get('/login', [\App\Http\Controllers\Admin\Auth\LoginController::class, 'showLoginForm'])->name('login');
Route::post('/login', [\App\Http\Controllers\Admin\Auth\LoginController::class, 'login'])->name('login.submit');
Route::post('/logout', [\App\Http\Controllers\Admin\Auth\LoginController::class, 'logout'])->name('logout');
// 需要认证的后台路由
Route::middleware(['auth:admin'])->group(function () {
Route::get('/dashboard', [\App\Http\Controllers\Admin\DashboardController::class, 'index'])->name('dashboard');
});
});
// 普通用户路由组
Route::prefix('user')->name('user.')->group(function () {
Route::get('/login', [\App\Http\Controllers\User\Auth\LoginController::class, 'showLoginForm'])->name('login');
Route::post('/login', [\App\Http\Controllers\User\Auth\LoginController::class, 'login'])->name('login.submit');
Route::post('/logout', [\App\Http\Controllers\User\Auth\LoginController::class, 'logout'])->name('logout');
Route::middleware(['auth:user'])->group(function () {
Route::get('/dashboard', [\App\Http\Controllers\User\DashboardController::class, 'index'])->name('dashboard');
});
});
// 商家路由组同理,前缀为merchant,守卫为merchant
Route::prefix('merchant')->name('merchant.')->group(function () {
Route::get('/login', [\App\Http\Controllers\Merchant\Auth\LoginController::class, 'showLoginForm'])->name('login');
Route::post('/login', [\App\Http\Controllers\Merchant\Auth\LoginController::class, 'login'])->name('login.submit');
Route::post('/logout', [\App\Http\Controllers\Merchant\Auth\LoginController::class, 'logout'])->name('logout');
Route::middleware(['auth:merchant'])->group(function () {
Route::get('/dashboard', [\App\Http\Controllers\Merchant\DashboardController::class, 'index'])->name('dashboard');
});
});这里需要注意,路由中使用auth:admin这样的中间件,指定使用admin守卫进行认证,只有该守卫登录的用户才能访问对应路由组。
第三步:自定义登录控制器实现重定向逻辑
每个角色的登录控制器需要重写登录成功后的重定向逻辑,同时指定使用的守卫。以Admin的LoginController为例,其他角色的控制器写法类似,只需修改对应的守卫和重定向路径。
<?php
namespace App\Http\Controllers\Admin\Auth;
use App\Http\Controllers\Controller;
use Illuminate\Foundation\Auth\AuthenticatesUsers;
use Illuminate\Http\Request;
class LoginController extends Controller
{
use AuthenticatesUsers;
// 指定登录成功后跳转的路径,这里是管理员后台首页
protected $redirectTo = '/admin/dashboard';
// 构造函数中指定使用的守卫
public function __construct()
{
$this->middleware('guest:admin')->except('logout');
}
// 重写showLoginForm方法,返回对应角色的登录视图
public function showLoginForm()
{
return view('admin.auth.login');
}
// 重写guard方法,指定使用的认证守卫
protected function guard()
{
return \Auth::guard('admin');
}
// 重写logout方法,退出后跳转到对应角色的登录页
public function logout(Request $request)
{
$this->guard()->logout();
$request->session()->invalidate();
$request->session()->regenerateToken();
return redirect('/admin/login');
}
}第四步:优化未授权访问的重定向策略
默认情况下,未登录用户访问受保护路由会跳转到/auth/login,我们需要根据访问的前缀,跳转到对应角色的登录页。可以通过自定义异常处理器实现这个逻辑。
<?php
namespace App\Exceptions;
use Illuminate\Foundation\Exceptions\Handler as ExceptionHandler;
use Illuminate\Auth\AuthenticationException;
use Illuminate\Http\Request;
class Handler extends ExceptionHandler
{
// 重写未认证异常的处理逻辑
protected function unauthenticated($request, AuthenticationException $exception)
{
// 如果是API请求,返回JSON响应
if ($request->expectsJson()) {
return response()->json(['message' => '未授权'], 401);
}
// 获取当前访问的路径,判断对应的前缀
$path = $request->path();
$guard = null;
if (strpos($path, 'admin/') === 0) {
$guard = 'admin';
} elseif (strpos($path, 'user/') === 0) {
$guard = 'user';
} elseif (strpos($path, 'merchant/') === 0) {
$guard = 'merchant';
}
// 根据守卫跳转到对应登录页
switch ($guard) {
case 'admin':
return redirect()->route('admin.login');
case 'user':
return redirect()->route('user.login');
case 'merchant':
return redirect()->route('merchant.login');
default:
return redirect('/');
}
}
}第五步:验证与测试
完成上述配置后,可以进行如下测试:
- 访问/admin/dashboard,未登录时会自动跳转到/admin/login,登录admin账户后跳转到/admin/dashboard
- 访问/user/dashboard,未登录时跳转到/user/login,登录user账户后跳转到/user/dashboard
- 在admin登录状态下访问/user/dashboard,会因为user守卫未认证,跳转到/user/login
- 同一浏览器中,先登录admin,再打开新标签页登录user,两个会话可以独立存在(因为使用了不同的守卫和会话配置)
注意事项
如果需要在登录页面显示错误提示,需要在对应的登录视图中添加错误提示代码,例如:
<!-- admin/auth/login.blade.php 部分代码 -->
@if ($errors->any())
<div class="alert alert-danger">
<ul>
@foreach ($errors->all() as $error)
<li>{{ $error }}</li>
@endforeach
</ul>
</div>
@endif另外,如果使用remember token功能,需要确保每个用户表的remember_token字段存在,并且模型中已经配置好hidden属性隐藏该字段。如果需要支持同一浏览器多角色登录,需要保证不同守卫使用的会话驱动是独立的,或者使用不同的session cookie名称,可以在config/session.php中针对不同守卫配置不同的cookie前缀。