本文目录导读:

在 Laravel Fortify 中使用无头认证(Headless Authentication),意味着不依赖 Fortify 自带的前端视图,而是通过 API 方式处理认证请求,以下是完整的使用指南:
安装和配置 Fortify
composer require laravel/fortify php artisan vendor:publish --provider="Laravel\Fortify\FortifyServiceProvider"
在 config/fortify.php 中配置:
'views' => false, // 禁用视图
'features' => [
Features::registration(),
Features::resetPasswords(),
Features::emailVerification(),
Features::updateProfileInformation(),
Features::updatePasswords(),
Features::twoFactorAuthentication(),
],
配置无头认证中间件
在 app/Http/Kernel.php 中:
protected $middlewareGroups = [
'api' => [
\Laravel\Sanctum\Http\Middleware\EnsureFrontendRequestsAreStateful::class,
'throttle:api',
\Illuminate\Routing\Middleware\SubstituteBindings::class,
],
];
创建认证控制器
// app/Http/Controllers/Auth/AuthController.php
namespace App\Http\Controllers\Auth;
use App\Http\Controllers\Controller;
use Illuminate\Http\Request;
use Illuminate\Support\Facades\Auth;
use Laravel\Fortify\Contracts\LoginResponse;
use Laravel\Fortify\Contracts\RegisterResponse;
use Laravel\Fortify\Actions\AttemptToAuthenticate;
use Laravel\Fortify\Actions\EnsureLoginIsNotThrottled;
use Laravel\Fortify\Actions\PrepareAuthenticatedSession;
class AuthController extends Controller
{
public function login(Request $request)
{
$request->validate([
'email' => ['required', 'email'],
'password' => ['required'],
]);
// 尝试认证
$userProvider = config('fortify.users.provider');
if (Auth::attempt($request->only('email', 'password'))) {
$request->session()->regenerate();
$user = Auth::user();
$token = $user->createToken('auth-token')->plainTextToken;
return response()->json([
'status' => 'success',
'message' => '登录成功',
'token' => $token,
'user' => $user
]);
}
return response()->json([
'status' => 'error',
'message' => '登录失败'
], 401);
}
public function register(Request $request)
{
$request->validate([
'name' => ['required', 'string', 'max:255'],
'email' => ['required', 'string', 'email', 'max:255', 'unique:users'],
'password' => ['required', 'string', 'min:8'],
]);
$user = \App\Models\User::create([
'name' => $request->name,
'email' => $request->email,
'password' => bcrypt($request->password),
]);
// 创建token
$token = $user->createToken('auth-token')->plainTextToken;
return response()->json([
'status' => 'success',
'message' => '注册成功',
'token' => $token,
'user' => $user
], 201);
}
public function logout(Request $request)
{
// 撤销当前token
$request->user()->currentAccessToken()->delete();
auth()->guard('web')->logout();
return response()->json([
'status' => 'success',
'message' => '退出成功'
]);
}
public function user(Request $request)
{
return response()->json([
'status' => 'success',
'user' => $request->user()
]);
}
}
路由配置
// routes/api.php
use App\Http\Controllers\Auth\AuthController;
Route::prefix('auth')->group(function () {
Route::post('/login', [AuthController::class, 'login'])->middleware('guest');
Route::post('/register', [AuthController::class, 'register'])->middleware('guest');
Route::middleware('auth:sanctum')->group(function () {
Route::post('/logout', [AuthController::class, 'logout']);
Route::get('/user', [AuthController::class, 'user']);
});
});
// 密码重置路由
Route::prefix('auth')->group(function () {
Route::post('/forgot-password', function (Request $request) {
$request->validate(['email' => 'required|email']);
$status = Password::sendResetLink(
$request->only('email')
);
return $status === Password::RESET_LINK_SENT
? response()->json(['status' => 'success', 'message' => '重置链接已发送'])
: response()->json(['status' => 'error', 'message' => '发送失败'], 400);
})->middleware('guest');
Route::post('/reset-password', function (Request $request) {
$request->validate([
'token' => 'required',
'email' => 'required|email',
'password' => 'required|min:8|confirmed',
]);
$status = Password::reset(
$request->only('email', 'password', 'password_confirmation', 'token'),
function ($user, $password) {
$user->forceFill([
'password' => Hash::make($password)
])->save();
}
);
return $status === Password::PASSWORD_RESET
? response()->json(['status' => 'success', 'message' => '密码重置成功'])
: response()->json(['status' => 'error', 'message' => '重置失败'], 400);
})->middleware('guest');
})->middleware('guest');
配置 CORS
// config/cors.php
return [
'paths' => ['api/*', 'sanctum/csrf-cookie'],
'allowed_methods' => ['*'],
'allowed_origins' => [env('FRONTEND_URL', 'http://localhost:3000')],
'allowed_origins_patterns' => [],
'allowed_headers' => ['*'],
'exposed_headers' => [],
'max_age' => 0,
'supports_credentials' => true,
];
前端请求示例
// src/api/auth.js
import axios from 'axios';
const api = axios.create({
baseURL: 'http://localhost:8000/api',
withCredentials: true,
});
// 登录
export const login = async (email, password) => {
const response = await api.post('/auth/login', { email, password });
// 存储token
localStorage.setItem('auth_token', response.data.token);
return response.data;
};
// 注册
export const register = async (name, email, password) => {
const response = await api.post('/auth/register', { name, email, password });
localStorage.setItem('auth_token', response.data.token);
return response.data;
};
// 获取用户信息
export const getUser = async () => {
const token = localStorage.getItem('auth_token');
const response = await api.get('/auth/user', {
headers: { Authorization: `Bearer ${token}` }
});
return response.data;
};
// 退出登录
export const logout = async () => {
const token = localStorage.getItem('auth_token');
await api.post('/auth/logout', null, {
headers: { Authorization: `Bearer ${token}` }
});
localStorage.removeItem('auth_token');
};
环境配置
# .env SANCTUM_STATEFUL_DOMAINS=localhost:3000 FRONTEND_URL=http://localhost:3000 SESSION_DOMAIN=localhost
非无头模式(保留视图响应)
如果你需要在无头认证中保留某些组件:
// app/Providers/FortifyServiceProvider.php
public function boot()
{
// 自定义登录回调
Fortify::authenticateUsing(function (Request $request) {
$user = \App\Models\User::where('email', $request->email)->first();
if ($user && \Illuminate\Support\Facades\Hash::check($request->password, $user->password)) {
return $user;
}
});
// 登录成功后的事件
Fortify::loginView(function () {
return response()->json(['message' => 'Login required']);
});
}
测试示例
// tests/Feature/AuthTest.php
public function test_login()
{
$user = \App\Models\User::factory()->create();
$response = $this->postJson('/api/auth/login', [
'email' => $user->email,
'password' => 'password',
]);
$response->assertStatus(200)
->assertJsonStructure(['status', 'token', 'user']);
}
关键要点
- 禁用视图:在
config/fortify.php中设置'views' => false - 使用 Sanctum:确保安装 Laravel Sanctum 用于 API token 认证
- 配置 CORS:正确配置跨域请求,特别是
supports_credentials - 状态保持:使用
EnsureFrontendRequestsAreStateful中间件保持会话状态 - Token 管理:返回的 token 用于后续请求的认证
这样,你就可以完全通过 API 方式使用 Fortify 的认证功能,而不需要依赖任何前端页面。