Laravel 11 默认没有创建 routes/api.php 文件,
初始化 API 配置
php artisan install:api
创建了 routes/api.php 文件,并在 bootstrap/app.php 文件中自动配置:
<?php use Illuminate\Auth\AuthenticationException; use Illuminate\Foundation\Application; use Illuminate\Foundation\Configuration\Exceptions; use Illuminate\Foundation\Configuration\Middleware; use Illuminate\Http\Request; use Symfony\Component\HttpFoundation\Response; use Symfony\Component\HttpKernel\Exception\NotFoundHttpException; return Application::configure(basePath: dirname(__DIR__)) ->withRouting( web: __DIR__ . '/../routes/web.php', api: __DIR__ . '/../routes/api.php', // 自动注册 commands: __DIR__ . '/../routes/console.php', health: '/up', ) ->withMiddleware(function (Middleware $middleware) { // }) ->withExceptions(function (Exceptions $exceptions) { // })->create();
进一步配置,在遇到未登录的情况返回错误提示而不是跳转到登录页面:
return Application::configure(basePath: dirname(__DIR__)) ->withRouting( web: __DIR__ . '/../routes/web.php', api: __DIR__ . '/../routes/api.php', commands: __DIR__ . '/../routes/console.php', health: '/up', ) ->withMiddleware(function (Middleware $middleware) { // }) ->withExceptions(function (Exceptions $exceptions) { // 401 返回错误提示 $exceptions->render(function (AuthenticationException $e, Request $request) { if ($request->is('api/*')) { return response()->json([ 'message' => $e->getMessage(), ], Response::HTTP_UNAUTHORIZED); } }); })->create();
安装设置 JWT 包
composer require php-open-source-saver/jwt-auth
发布配置文件
php artisan vendor:publish --provider="PHPOpenSourceSaver\JWTAuth\Providers\LaravelServiceProvider"
生成 secret key,保存在 .env 文件中:
php artisan jwt:secret
在 config/auth.php 文件中修改 auth guard 配置:
'defaults' => [ 'guard' => 'api', 'passwords' => 'users', ], 'guards' => [ 'web' => [ 'driver' => 'session', 'provider' => 'users', ], 'api' => [ 'driver' => 'jwt', 'provider' => 'users', ], ],
完善用户(User)模型
实现 JWTSubject 接口:
<?php namespace App\Models; // use Illuminate\Contracts\Auth\MustVerifyEmail; use Illuminate\Database\Eloquent\Factories\HasFactory; use Illuminate\Foundation\Auth\User as Authenticatable; use Illuminate\Notifications\Notifiable; use PHPOpenSourceSaver\JWTAuth\Contracts\JWTSubject; class User extends Authenticatable implements JWTSubject { use HasFactory, Notifiable; /** * The attributes that are mass assignable. * * @var array<int, string> */ protected $fillable = [ 'name', 'email', 'password', ]; /** * The attributes that should be hidden for serialization. * * @var array<int, string> */ protected $hidden = [ 'password', 'remember_token', ]; /** * Get the attributes that should be cast. * * @return array<string, string> */ protected function casts(): array { return [ 'email_verified_at' => 'datetime', 'password' => 'hashed', ]; } /** * Get the identifier that will be stored in the subject claim of the JWT. * * @return mixed */ public function getJWTIdentifier() { return $this->getKey(); } /** * Return a key value array, containing any custom claims to be added to the JWT. * * @return array */ public function getJWTCustomClaims() { return []; } }
创建用户相关的 API
php artisan make:controller AuthController
完善 app/Http/Controllers/AuthController.php:
<?php namespace App\Http\Controllers; use App\Http\Controllers\Controller; use App\Models\User; use Validator; class AuthController extends Controller { /** * Register a User. * * @return \Illuminate\Http\JsonResponse */ public function register() { $validator = Validator::make(request()->all(), [ 'name' => 'required', 'email' => 'required|email|unique:users', 'password' => 'required|confirmed|min:8', ]); if($validator->fails()){ return response()->json($validator->errors()->toJson(), 400); } $user = new User; $user->name = request()->name; $user->email = request()->email; $user->password = bcrypt(request()->password); $user->save(); return response()->json($user, 201); } /** * Get a JWT via given credentials. * * @return \Illuminate\Http\JsonResponse */ public function login() { $credentials = request(['email', 'password']); if (! $token = auth()->attempt($credentials)) { return response()->json(['error' => 'Unauthorized'], 401); } return $this->respondWithToken($token); } /** * Get the authenticated User. * * @return \Illuminate\Http\JsonResponse */ public function me() { return response()->json(auth()->user()); } /** * Log the user out (Invalidate the token). * * @return \Illuminate\Http\JsonResponse */ public function logout() { auth()->logout(); return response()->json(['message' => 'Successfully logged out']); } /** * Refresh a token. * * @return \Illuminate\Http\JsonResponse */ public function refresh() { return $this->respondWithToken(auth()->refresh()); } /** * Get the token array structure. * * @param string $token * * @return \Illuminate\Http\JsonResponse */ protected function respondWithToken($token) { return response()->json([ 'access_token' => $token, 'token_type' => 'bearer', 'expires_in' => auth()->factory()->getTTL() * 60 ]); } }
注册路由
在 routes/api.php 文件中添加:
<?php use Illuminate\Support\Facades\Route; use App\Http\Controllers\AuthController; Route::group([ 'middleware' => 'api', 'prefix' => 'auth' ], function ($router) { Route::post('/register', [AuthController::class, 'register'])->name('register'); Route::post('/login', [AuthController::class, 'login'])->name('login'); Route::post('/logout', [AuthController::class, 'logout'])->middleware('auth:api')->name('logout'); Route::post('/refresh', [AuthController::class, 'refresh'])->middleware('auth:api')->name('refresh'); Route::post('/me', [AuthController::class, 'me'])->middleware('auth:api')->name('me'); });