它的本质是:**Auth 中间件不是“验证逻辑”本身,而是 **身份验证结果的守门人 (Gatekeeper)。
- 核心矛盾:HTTP 是无状态的。服务器如何知道当前请求是谁发出的?
- 解决方案:中间件调用Auth Guard(如 SessionGuard 或 TokenGuard)。Guard 负责从请求中提取凭证(Cookie/Token),验证其有效性,并返回一个
User对象或null。中间件根据这个结果决定是放行 (Pass)还是拦截 (Block)。 - 核心逻辑:别把 Auth 中间件当成“登录检查”。把它当成权限过滤器。它不关心你怎么登录,只关心现在是不是已登录状态。真正的验证逻辑在 Guard 里,中间件只是执行“如果没登录,就踢出去”的策略。
如果把 Web 应用比作私人俱乐部:
- Request:是访客。
- Auth Middleware:是门口保安。
- 保安不看身份证原件(那是 Guard 的事)。
- 保安只看会员手环(Session/Token)。
- 有手环?请进 (
$next($request))。 - 没手环?去前台办卡 (
Redirect to Login) 或 滚蛋 (401 Unauthorized)。
- Auth Guard:是发卡中心/验证机。
- 检查手环是不是伪造的。
- 查数据库确认持卡人是否被除名。
- 核心逻辑:中间件负责流程控制,Guard 负责事实认定。
一、中间件结构:Authenticate类
Laravel 的 Auth 中间件位于Illuminate\Auth\Middleware\Authenticate。
1. 核心方法:handle()
publicfunctionhandle($request,Closure$next,...$guards){// 1. 尝试认证$this->authenticate($request,$guards);// 2. 认证成功,放行return$next($request);}- 参数
$guards:允许指定使用哪个守卫。如auth:api使用apiguard,auth使用默认 guard。
2. 认证逻辑:authenticate()
protectedfunctionauthenticate($request,array$guards){if(empty($guards)){$guards=[null];// 使用默认 guard}foreach($guardsas$guard){// 尝试通过指定 guard 认证if($this->auth->guard($guard)->check()){// 成功则设置当前 guard 并返回return$this->auth->shouldUse($guard);}}// 所有 guard 都失败,抛出异常$this->unauthenticated($request,$guards);}- 关键点:
- 多 Guard 支持:你可以同时尝试
web和api,只要有一个成功就算通过。 check()方法:这是核心。它调用 Guard 的check(),判断用户是否已登录。unauthenticated():失败时的处理逻辑。
- 多 Guard 支持:你可以同时尝试
二、Guard 交互:中间件如何与 Guard 对话?
中间件本身不存状态,它依赖Auth Manager(Illuminate\Auth\AuthManager) 获取 Guard。
1. 获取 Guard
- 代码:
$this->auth->guard($name) - 机制:
AuthManager是一个工厂。- 根据配置 (
config/auth.php) 创建对应的 Guard 实例(如SessionGuard或TokenGuard)。 - 单例缓存,避免重复创建。
2. 调用check()
代码:
$guard->check()SessionGuard 实现:
publicfunctioncheck(){return!is_null($this->user());}- 它调用
$this->user()。 user()方法会检查 Session 中是否有用户 ID。- 如果有,且尚未加载 User 对象,则通过 ID 查询数据库并缓存。
- 返回 User 对象或 null。
- 它调用
TokenGuard (API) 实现:
- 从 Request Header (
Authorization: Bearer ...) 或 Query String 提取 Token。 - 验证 Token 有效性(查库或解密 JWT)。
- 返回 User 对象或 null。
- 从 Request Header (
💡 核心洞察:中间件只问 Guard:“这人是谁?” Guard 回答:“是 Alice” 或 “不认识”。中间件根据回答决定开门还是关门。
三、未认证处理:unauthenticated()
当所有 Guard 都返回null时,触发此方法。
1. 默认行为
protectedfunctionunauthenticated($request,array$guards){thrownewAuthenticationException('Unauthenticated.',$guards,$this->redirectTo($request));}- 抛出异常:
AuthenticationException。 - 为什么抛异常而不是直接 redirect?
- 因为中间件不知道客户端是浏览器还是 API 调用者。
- 浏览器需要 HTML 重定向。
- API 需要 JSON 401 响应。
- 异常交给全局异常处理器 (
Handler::render) 统一处理。
2. 异常渲染 (App\Exceptions\Handler)
protectedfunctionunauthenticated($request,AuthenticationException$exception){if($request->expectsJson()){returnresponse()->json(['message'=>'Unauthenticated.'],401);}returnredirect()->guest($exception->redirectTo()??route('login'));}- 智能判断:
- 检查
Accept: application/json头。 - 如果是 API,返回 JSON。
- 如果是 Web,重定向到登录页。
- 检查
四、认证驱动:Session vs Token
1. SessionGuard (Web)
- 凭证来源:Session Cookie (
laravel_session)。 - 验证方式:
- 从 Session 读取
login_web_59ba36addc2b2f9401580f014c7f58ea4e30989d_id。 - 通过 ID 查询 Users 表。
- 如果找到,存入内存缓存,下次请求直接用。
- 从 Session 读取
- 特点:有状态,依赖 Cookie。
2. TokenGuard / Sanctum / Passport (API)
- 凭证来源:
AuthorizationHeader。 - 验证方式:
- Sanctum:查找
personal_access_tokens表,哈希匹配 Token。 - Passport (JWT):解密 JWT,验证签名和过期时间。
- Sanctum:查找
- 特点:无状态(或半无状态),适合移动端/SPA。
五、自定义策略:如何扩展 Auth 中间件?
1. 自定义重定向逻辑
- 场景:未登录时重定向到自定义页面,而非默认
login。 - 方法:在
App\Http\Middleware\Authenticate中重写redirectTo()方法。protectedfunctionredirectTo($request){if(!$request->expectsJson()){returnroute('custom.login');}}
2. 自定义 Guard
- 场景:使用特殊的认证方式(如 LDAP, SSO)。
- 步骤:
- 实现
Illuminate\Contracts\Auth\Guard接口。 - 在
AuthServiceProvider中注册:Auth::extend('ldap',function($app,$name,array$config){returnnewLdapGuard(...);}); - 在
config/auth.php中配置。 - 路由中使用:
->middleware('auth:ldap')。
- 实现
🚀 总结:原子化“Laravel Auth Middleware”全景图
| 维度 | 关键点 |
|---|---|
| 本质 | 基于 Guard 认证结果的身份访问控制过滤器 |
| 核心流程 | Handle -> Authenticate (Loop Guards) -> Check (Guard) -> Unauthenticated (Exception) |
| 关键类 | Authenticate(Middleware),AuthManager,SessionGuard/TokenGuard |
| 主要价值 | 解耦认证逻辑与业务逻辑,支持多 Guard,智能响应 (JSON/Redirect) |
| 异常处理 | 抛出AuthenticationException,由 Handler 统一渲染 |
| PHP 隐喻 | Security Guard (Middleware) checking Membership Card (Guard) |
| 公式 | Access = (Guard_Check × Exception_Handling) ^ Multi_Guard_Support |
终极心法:
Auth 中间件的本质,是“对身份的信任边界”。
它不生产身份,它只验证身份的合法性。
它将复杂的认证细节封装在 Guard 之后,暴露简单的通过/拒绝接口。
于守卫中见安全,于异常中见灵活;以契约为尺,解混乱之牛,于访问控制中,求严谨之真。
行动指令:
- 阅读源码:打开
vendor/laravel/framework/src/Illuminate/Auth/Middleware/Authenticate.php,逐行阅读handle和authenticate方法。 - 调试 Guard:在
SessionGuard::user()处打断点,观察它是如何从 Session 恢复用户对象的。 - 测试 API:移除 Header 中的 Token,观察返回的 JSON 401 响应,理解
expectsJson()的判断逻辑。 - 思维升级:记住,中间件只是保安,Guard 才是警察。理解两者的分工,你才能设计出灵活且安全的认证系统。