You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Laravel 11多角色认证问题:仅学生可登录,管理员/超管认证失败

Laravel 11 多角色认证故障排查与修复

在Laravel 11中搭建了包含student、admin、super_admin角色的多角色认证系统(未使用默认User模型),目前仅学生账号能正常完成登录认证,管理员与超级管理员认证失败,且Auth::user()返回空值。


相关代码文件

AuthenticatedSessionController

class AuthenticatedSessionController extends Controller
{
    public function create(): View
    {
        return view('auth.login');
    }
    /**
     * Handle an incoming authentication request.
     */
    public function store(LoginRequest $request): RedirectResponse
    {
        $request->authenticate();

        $request->session()->regenerate();

        // Retrieve the authenticated user from the correct guard
        $user = Auth::user();

        // Check user's role and redirect accordingly
        if ($user instanceof \App\Models\Student && $user->hasRole('student')) {
            return redirect()->intended(route('student.dashboard'));
        } elseif ($user instanceof \App\Models\Admin && $user->hasRole('academichead')) {
            return redirect()->intended(route('admin.dashboard'));
        } elseif ($user instanceof \App\Models\SuperAdmin && $user->hasRole('registrar')) {
            return redirect()->intended(route('superadmin.dashboard'));
        } else {
            // Default redirect if no specific role match found
            return redirect()->intended('/');
        }
    }
    /**
     * Destroy an authenticated session.
     */
    public function destroy(Request $request)
    {
        Auth::guard('web')->logout();

        $request->session()->invalidate();

        $request->session()->regenerateToken();

        return redirect('/');
    }
}

LoginController

class LoginController extends Controller
{
    public function showLoginForm()
    {
        return view('auth.login');
    }

    public function login(Request $request)
    {
        $request->validate([
            'email' => 'required|email',
            'password' => 'required',
        ]);

        $credentials = $request->only('email', 'password');

        if (Auth::guard('academichead')->attempt($credentials)) {
            return redirect()->intended('/admin/dashboard');
        } elseif (Auth::guard('registrar')->attempt($credentials)) {
            return redirect()->intended('/superadmin/dashboard');
        } elseif (Auth::guard('web')->attempt($credentials)) {
            return redirect()->intended('/user/dashboard');
        }

        throw ValidationException::withMessages([
            'email' => [trans('auth.failed')],
        ]);
    }

    public function logout()
    {
        if (Auth::guard('academichead')->check()) {
            Auth::guard('academichead')->logout();
        } elseif (Auth::guard('registrar')->check()) {
            Auth::guard('registrar')->logout();
        } else {
            Auth::guard('web')->logout();
        }

        return redirect('/');
    }
}

LoginRequest.php

class LoginRequest extends FormRequest
{
    protected $guard;

    /**
     * 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\Rule|array|string>
     */
    public function rules(): array
    {
        return [
            'email' => ['required', 'string', 'email'],
            'password' => ['required', 'string'],
        ];
    }

    /**
     * Attempt to authenticate the request's credentials.
     *
     * @throws \Illuminate\Validation\ValidationException
     */
    public function authenticate(): void
    {
        $this->ensureIsNotRateLimited();

        $credentials = $this->only('email', 'password');

        if (Auth::guard('academichead')->attempt($credentials)) {
            $this->guard = 'academichead';
        } elseif (Auth::guard('registrar')->attempt($credentials)) {
            $this->guard = 'registrar';
        } elseif (Auth::guard('web')->attempt($credentials)) {
            $this->guard = 'web';
        } else {
            RateLimiter::hit($this->throttleKey());

            throw ValidationException::withMessages([
                'email' => trans('auth.failed'),
            ]);
        }

        RateLimiter::clear($this->throttleKey());
    }

    /**
     * Ensure the login request is not rate limited.
     *
     * @throws \Illuminate\Validation\ValidationException
     */
    public function ensureIsNotRateLimited(): void
    {
        if (!RateLimiter::tooManyAttempts($this->throttleKey(), 5)) {
            return;
        }

        event(new Lockout($this));

        $seconds = RateLimiter::availableIn($this->throttleKey());

        throw ValidationException::withMessages([
            'email' => trans('auth.throttle', [
                'seconds' => $seconds,
                'minutes' => ceil($seconds / 60),
            ]),
        ]);
    }

    /**
     * Get the rate limiting throttle key for the request.
     */
    public function throttleKey(): string
    {
        return Str::transliterate(Str::lower($this->string('email')) . '|' . $this->ip());
    }

    /**
     * Get the authenticated guard.
     */
    public function authenticatedGuard(): string
    {
        return $this->guard;
    }
}

web.php

Route::get('/', function () {
    return view('welcome');
});

Route::get('login', [LoginController::class, 'showLoginForm'])->name('login');
Route::post('login', [LoginController::class, 'login'])->name('login.post');
Route::post('logout', [LoginController::class, 'logout'])->name('logout');

Route::prefix('student')->middleware(['auth'])->group(function () {
    Route::get('/dashboard', [StudentController::class, 'index'])->name('student.dashboard');
});

Route::prefix('academichead')->middleware(['auth:academichead'])->group(function () {
    Route::get('/dashboard', [AdminDashboardController::class, 'index'])->name('admin.dashboard');
});

Route::prefix('registrar')->middleware(['auth:registrar'])->group(function () {
    Route::get('/dashboard', [SuperAdminController::class, 'index'])->name('superadmin.dashboard');
});

Route::get('/dashboard', function () {
    return view('dashboard');
})->middleware(['auth', 'verified'])->name('dashboard');

Route::middleware('auth')->group(function () {
    Route::get('/profile', [ProfileController::class, 'edit'])->name('profile.edit');
    Route::patch('/profile', [ProfileController::class, 'update'])->name('profile.update');
    Route::delete('/profile', [ProfileController::class, 'destroy'])->name('profile.destroy');
});

require __DIR__ . '/auth.php';

故障排查与修复方案

1. 清理冗余认证逻辑与路由冲突

当前代码同时存在自定义LoginController和Laravel默认的AuthenticatedSessionController,且web.php中同时注册了自定义登录路由与默认auth.php路由,导致认证逻辑混乱。

修复:

  • 保留一套认证逻辑即可,建议删除默认auth.php的引入(注释或删除require __DIR__ . '/auth.php';),统一使用自定义LoginController处理登录登出。

2. 认证成功后设置默认Guard

LoginRequest中认证成功后仅记录了Guard名称,但未将其设为当前请求的默认Guard,导致后续Auth::user()无法获取对应Guard的用户实例。

修复:修改LoginRequest的authenticate()方法

public function authenticate(): void
{
    $this->ensureIsNotRateLimited();

    $credentials = $this->only('email', 'password');

    if (Auth::guard('academichead')->attempt($credentials)) {
        $this->guard = 'academichead';
        Auth::shouldUse('academichead'); // 设置当前请求默认Guard
    } elseif (Auth::guard('registrar')->attempt($credentials)) {
        $this->guard = 'registrar';
        Auth::shouldUse('registrar');
    } elseif (Auth::guard('web')->attempt($credentials)) {
        $this->guard = 'web';
        Auth::shouldUse('web');
    } else {
        RateLimiter::hit($this->throttleKey());

        throw ValidationException::withMessages([
            'email' => trans('auth.failed'),
        ]);
    }

    RateLimiter::clear($this->throttleKey());
}

3. 适配多Guard的用户获取与登出

  • AuthenticatedSessionController的store方法可通过指定Guard获取用户,或依赖已设置的默认Guard直接调用Auth::user();
  • destroy方法需适配多Guard登出,而非仅处理web Guard。

修复:修改AuthenticatedSessionController的store与destroy方法

public function store(LoginRequest $request): RedirectResponse
{
    $request->authenticate();

    $request->session()->regenerate();

    $user = Auth::user(); // 已设置默认Guard,直接获取

    if ($user instanceof \App\Models\Student && $user->hasRole('student')) {
        return redirect()->intended(route('student.dashboard'));
    } elseif ($user instanceof \App\Models\Admin && $user->hasRole('academichead')) {
        return redirect()->intended(route('admin.dashboard'));
    } elseif ($user instanceof \App\Models\SuperAdmin && $user->hasRole('registrar')) {
        return redirect()->intended(route('superadmin.dashboard'));
    } else {
        return redirect()->intended('/');
    }
}

public function destroy(Request $request)
{
    // 获取当前认证的Guard
    $guard = Auth::getDefaultDriver();
    Auth::guard($guard)->logout();

    $request->session()->invalidate();
    $request->session()->regenerateToken();

    return redirect('/');
}

4. 确认Guard配置正确性

检查config/auth.php中的Guard与Provider配置,确保每个Guard对应正确的模型:

'guards' => [
    'web' => [
        'driver' => 'session',
        'provider' => 'students',
    ],
    'academichead' => [
        'driver' => 'session',
        'provider' => 'admins',
    ],
    'registrar' => [
        'driver' => 'session',
        'provider' => 'super_admins',
    ],
],

'providers' => [
    'students' => [
        'driver' => 'eloquent',
        'model' => App\Models\Student::class,
    ],
    'admins' => [
        'driver' => 'eloquent',
        'model' => App\Models\Admin::class,
    ],
    'super_admins' => [
        'driver' => 'eloquent',
        'model' => App\Models\SuperAdmin::class,
    ],
],

内容的提问来源于stack exchange,提问作者aaron nim

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.06.22 15:44:53