在 Laravel 中,用户认证体系由 Auth 组件统一管理。登录成功后,框架把用户实例保存到对应的 guard 里,后续请求可通过多种方式读取当前用户信息。这些方式看起来只是写法不同,但在不同 guard、不同认证场景中可能会有细微差异,选对方法能少踩很多空值和权限判断的坑。

一、从 Auth 门面和 auth 辅助函数读取
最常用的写法是调用 Auth::user() 或者 auth()->user()。这两个方法都会返回当前 guard 中已经通过认证的用户模型实例。如果用户已经登录,返回的就是 App\Models\User 对象;如果当前没有登录用户,返回值是 null。因此在读取用户字段前,最好先做空值判断。
use Illuminate\Support\Facades\Auth;
$user = Auth::user();
if ($user) {
echo $user->name;
}
$userId = Auth::id();
if (Auth::check()) {
// 已经登录,可以继续处理
}
Auth 门面和 auth 辅助函数的本质相同,都是通过 Laravel 的 AuthManager 解析出当前默认 guard。默认情况下,框架的默认 guard 是 web,也就是基于 session 的认证方式。如果应用主要使用传统的网页登录,这种写法非常直接。但如果后续增加了 API 认证,默认 guard 没有切换过来时,Auth::user() 在 API 路由中可能拿不到 token 对应的用户。
另外,Auth::id() 是 Auth::user() 的快捷方法,只返回用户主键,适合只需要用户 ID 的场景。它同样在未登录时返回 null。对于已经确定用户一定存在的业务代码,可以直接使用用户对象;对于可能存在匿名访问的接口,则需要配合 Auth::check() 或自定义判断来避免空指针报错。
二、在控制器与请求对象中获取用户
在控制器代码里,从请求对象读取用户信息是更贴合 Laravel 生命周期的方式。请求对象知道当前路由使用的是哪个 guard,因此 $request->user() 在 web 路由中返回 session 用户,在 api 路由中返回 token 用户。控制器方法可以注入 Illuminate\Http\Request 实例,然后调用 user 方法。
use Illuminate\Http\Request;
public function profile(Request $request)
{
$user = $request->user();
return response()->json([
'id' => $user->id,
'name' => $user->name,
]);
}
请求对象还支持在调用时显式指定 guard。例如在同时支持网页登录和 API 登录的控制器中,可以写 $request->user('sanctum') 来明确获取 Sanctum guard 下的用户。这种显式指定的方式比依赖默认 guard 更不容易出错,尤其是在路由分组混合使用多个 guard 的项目里。
另一种常见做法是在控制器方法中直接依赖注入 User 模型,但这里有一个容易混淆的地方:直接注入 User 模型默认并不会自动得到当前登录用户。Laravel 的容器只会尝试解析一个空的模型实例,除非你已经通过自定义绑定或路由模型绑定让它指向当前用户。因此,想要获取当前用户,更推荐使用 Auth::user()、auth()->user() 或 $request->user(),而不是依赖普通的模型注入。
三、多 guard 与 API 认证场景
当应用同时存在网页端和 API 端时,往往需要配置多个 guard。比如 web guard 负责 session 登录,api guard 负责 token 认证。如果在 API 路由中仍然使用 Auth::user(),而默认 guard 仍然是 web,就很容易拿到 null。此时应该使用 Auth::guard('api')->user() 来读取对应 guard 下的用户。
$apiUser = Auth::guard('api')->user();
$webUser = Auth::guard('web')->user();
// 或者从请求对象指定 guard
$user = $request->user('sanctum');
如果项目使用 Laravel Sanctum 做 API 认证,情况会更灵活。路由加上 auth:sanctum 中间件后,$request->user() 会自动识别请求中的 token,并返回对应用户。Sanctum 同时还支持基于 session 的 SPA 认证,因此同一个接口可能既能用 token 访问,也能通过 session 访问。这种场景下,统一使用 $request->user() 通常比写死 guard 更合适。
需要特别注意的是,API 认证未通过时,受保护的接口通常会在中间件层直接返回 401,而不是执行到控制器再返回 null。因此控制器里如果能执行到获取用户的代码,说明认证已经通过。但如果你希望在同一个控制器中同时处理匿名和登录两种状态,就需要在代码里判断用户是否为 null。
四、容易踩的细节与推荐做法
未登录状态下,Auth::user() 返回 null,这是最常见的问题来源。比如记录操作日志时,如果直接写 Auth::user()->id,匿名请求就会触发 Trying to get property 'id' of non-object 错误。推荐使用 optional() 辅助函数或先判断后取值。这样即便用户未登录,代码也能安全执行。
if ($user = Auth::user()) {
Log::info('当前用户ID: ' . $user->id);
}
// 使用 optional 避免 null 报错
$name = optional(Auth::user())->name;
另一个容易被忽略的时机问题是服务提供者。不要在 AppServiceProvider 的 boot 阶段读取当前用户,因为此时请求尚未完整初始化,认证信息可能还没有准备好。同样,在队列任务中也无法直接使用 Auth 门面,因为任务运行在 CLI 进程里,没有 session 和请求上下文。正确的做法是在派发任务时把用户 ID 作为参数传入,任务执行时再通过模型查询用户。
最后是关于缓存。用户模型可能包含密码、token 等敏感字段,不建议直接缓存整个用户实例。可以缓存用户 ID,需要时再从数据库重新查询。这样既能减少 session 或缓存中暴露过多数据,也能避免模型序列化带来的额外开销。对于需要频繁读取当前用户信息的场景,合理利用 ID 查询和必要的字段裁剪,比简单缓存整个模型更加稳妥。