PHP项目Laravel Fortify无头认证怎么用

wen PHP项目 3

本文目录导读:

PHP项目Laravel Fortify无头认证怎么用

  1. 安装和配置 Fortify
  2. 配置无头认证中间件
  3. 创建认证控制器
  4. 路由配置
  5. 配置 CORS
  6. 前端请求示例
  7. 环境配置
  8. 非无头模式(保留视图响应)
  9. 测试示例
  10. 关键要点

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

关键要点

  1. 禁用视图:在 config/fortify.php 中设置 'views' => false
  2. 使用 Sanctum:确保安装 Laravel Sanctum 用于 API token 认证
  3. 配置 CORS:正确配置跨域请求,特别是 supports_credentials
  4. 状态保持:使用 EnsureFrontendRequestsAreStateful 中间件保持会话状态
  5. Token 管理:返回的 token 用于后续请求的认证

这样,你就可以完全通过 API 方式使用 Fortify 的认证功能,而不需要依赖任何前端页面。

抱歉,评论功能暂时关闭!