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

Laravel Breeze下User与Client共享认证服务实现方案

Laravel Breeze 双表(Users/Clients)认证实现方案

核心问题:你当前仅新增了clients认证提供者,但默认web guard仍绑定users表,$request->authenticate()默认走当前默认guard的校验逻辑,自然不会查询clients表,且你之前的跳转逻辑写在authenticate()调用之后,根本走不到客户端校验分支。


第一步:完善auth.php配置,新增客户端专属guard

你当前配置的guards数组缺少客户端对应的guard,修改config/auth.php如下,改完执行php artisan config:clear清除配置缓存:

return [
    'defaults' => [
        'guard' => 'web',
        'passwords' => 'users',
    ],
    'guards' => [
        'web' => [
            'driver' => 'session',
            'provider' => 'users',
        ],
        // 新增客户端专属guard
        'client' => [
            'driver' => 'session',
            'provider' => 'clients',
        ],
    ],
    // providers部分你已配置正确,无需修改
    'providers' => [
        'users' => [
            'driver' => 'eloquent',
            'model' => App\Models\User::class,
        ],
        'clients' => [
            'driver' => 'eloquent',
            'model' => App\Models\Client::class,
        ],
    ],
    // passwords部分你已配置正确,无需修改
    'passwords' => [
        'users' => [
            'provider' => 'users',
            'table' => 'password_resets',
            'expire' => 60,
            'throttle' => 60,
        ],
        'clients' => [
            'provider' => 'clients',
            'table' => 'password_resets',
            'expire' => 60,
            'throttle' => 60,
        ],
    ],
    'password_timeout' => 10800,
];

第二步:确保Client模型满足认证要求

Laravel认证逻辑要求模型必须实现Illuminate\Contracts\Auth\Authenticatable接口,直接复用框架自带的认证基类即可,打开app/Models/Client.php确认代码如下:

<?php

namespace App\Models;

use Illuminate\Foundation\Auth\User as Authenticatable;
use Illuminate\Notifications\Notifiable;
use Illuminate\Contracts\Auth\MustVerifyEmail;

// 继承Authenticatable基类就自带所有认证所需方法
class Client extends Authenticatable implements MustVerifyEmail
{
    use Notifiable;

    protected $fillable = [
        'name',
        'email',
        'password',
    ];

    protected $hidden = [
        'password',
        'remember_token',
    ];

    protected $casts = [
        'email_verified_at' => 'datetime',
    ];
}

注意:clients表必须包含id、email、password、remember_token、email_verified_at这些认证必需字段,缺的字段补写迁移文件添加即可。


第三步:拆分独立登录入口,分离认证逻辑

不要把管理员和客户端的登录逻辑塞在同一个控制器方法里,分开写逻辑能避免绝大多数认证冲突问题:

  1. 首先在routes/web.php拆分路由组:
// 管理员登录路由,走默认web guard,复用Breeze自带控制器即可
Route::middleware('guest')->group(function () {
    Route::get('/login', [AuthenticatedSessionController::class, 'create'])->name('login');
    Route::post('/login', [AuthenticatedSessionController::class, 'store']);
});

// 客户端登录路由,走client guard,使用独立控制器
Route::middleware('guest:client')->prefix('portal')->name('portal.')->group(function () {
    Route::get('/login', [ClientAuthenticatedSessionController::class, 'create'])->name('login');
    Route::post('/login', [ClientAuthenticatedSessionController::class, 'store'])->name('login.store');
});
  1. 新建客户端专属登录控制器app/Http/Controllers/Auth/ClientAuthenticatedSessionController.php,逻辑和Breeze自带控制器一致,仅指定使用client guard:
<?php

namespace App\Http\Controllers\Auth;

use App\Http\Controllers\Controller;
use App\Http\Requests\Auth\ClientLoginRequest;
use Illuminate\Http\RedirectResponse;
use Illuminate\Http\Request;
use Illuminate\Support\Facades\Auth;
use Illuminate\View\View;

class ClientAuthenticatedSessionController extends Controller
{
    public function create(): View
    {
        return view('portal.login'); // 对应客户端专属登录页模板
    }

    public function store(ClientLoginRequest $request): RedirectResponse
    {
        $request->authenticate();
        $request->session()->regenerate();
        // 登录成功直接跳转到客户端对应portal页面
        return redirect()->route('portal.show', auth('client')->id());
    }

    public function destroy(Request $request): RedirectResponse
    {
        Auth::guard('client')->logout();
        $request->session()->invalidate();
        $request->session()->regenerateToken();
        return redirect('/portal/login');
    }
}
  1. 新建客户端专属登录请求类app/Http/Requests/Auth/ClientLoginRequest.php,核心是在认证方法中指定使用client guard校验:
<?php

namespace App\Http\Requests\Auth;

use Illuminate\Auth\Events\Lockout;
use Illuminate\Foundation\Http\FormRequest;
use Illuminate\Support\Facades\Auth;
use Illuminate\Support\Facades\RateLimiter;
use Illuminate\Support\Str;
use Illuminate\Validation\ValidationException;

class ClientLoginRequest extends FormRequest
{
    public function authorize(): bool
    {
        return true;
    }

    public function rules(): array
    {
        return [
            'email' => ['required', 'string', 'email'],
            'password' => ['required', 'string'],
        ];
    }

    public function authenticate(): void
    {
        $this->ensureIsNotRateLimited();
        // 指定client guard认证,就会自动查询clients表
        if (! Auth::guard('client')->attempt($this->only('email', 'password'), $this->boolean('remember'))) {
            RateLimiter::hit($this->throttleKey());
            throw ValidationException::withMessages([
                'email' => trans('auth.failed'),
            ]);
        }
        RateLimiter::clear($this->throttleKey());
    }

    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),
            ]),
        ]);
    }

    public function throttleKey(): string
    {
        // 加client后缀,避免和管理员登录共享限流计数
        return Str::transliterate(Str::lower($this->input('email')).'|'.$this->ip().'|client');
    }
}

第四步:修改中间件逻辑,区分不同guard的跳转规则

  1. 打开app/Http/Middleware/RedirectIfAuthenticated.php,修改handle方法,让不同guard的已登录用户跳转到对应页面:
public function handle(Request $request, Closure $next, string ...$guards): Response
{
    $guards = empty($guards) ? [null] : $guards;
    foreach ($guards as $guard) {
        if (Auth::guard($guard)->check()) {
            if ($guard == 'web') {
                return redirect(RouteServiceProvider::HOME);
            }
            if ($guard == 'client') {
                return redirect()->route('portal.show', Auth::guard($guard)->id());
            }
        }
    }
    return $next($request);
}
  1. 打开app/Exceptions/Handler.php,修改未认证用户的跳转逻辑,避免客户端用户未登录时被跳转到管理员登录页:
protected function unauthenticated($request, AuthenticationException $exception)
{
    if ($request->expectsJson()) {
        return response()->json(['message' => 'Unauthenticated.'], 401);
    }
    // 根据guard类型跳转到对应登录页
    if (in_array('client', $exception->guards())) {
        return redirect()->guest(route('portal.login'));
    }
    return redirect()->guest(route('login'));
}

额外注意事项

  • 邮箱验证、密码重置逻辑要对应指定guard,比如客户端发密码重置邮件时要调用Password::broker('clients'),不要用默认的users broker
  • 获取客户端登录用户信息统一用auth('client')->user(),不要直接用Auth::user(),后者默认读取web guard的登录用户
  • 路由鉴权时,客户端路由加auth:client中间件,管理员路由加auth:web中间件,避免权限混淆

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.09.03 03:39:23