Laravel 11 JWT(JSON WEB TOKEN) 身份验证
本篇文章中学习在 Laravel 11 中使用 JWT 进行 API 身份验证。这里使用 php-open-source-saver/jwt-auth
包进行 JWT 验证。
第一步:创建项目
$ laravel new jwtdemo
$ cd jwtdemo
第二步:安装 API 路由
$ php artisan install:api
第三步:验证用户身份
bootstrap/app.php
<?php
use Illuminate\Foundation\Application;
use Illuminate\Foundation\Configuration\Exceptions;
use Illuminate\Foundation\Configuration\Middleware;
use Illuminate\Auth\AuthenticationException;
use Illuminate\Http\Request;
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) {
$exceptions->render(function (AuthenticationException $e, Request $request) {
// 如果用户是非法 token,也会走个异常
if ($request->is('api/*')) {
return response()->json([
'message' => $e->getMessage(),
], 401);
}
});
})->create();
第四步:安装 jwt 包
安装 jwt 包
$ composer require php-open-source-saver/jwt-auth
生成配置文件
$ php artisan vendor:publish --provider="PHPOpenSourceSaver\JWTAuth\Providers\LaravelServiceProvider"
生成密钥
$ php artisan jwt:secret
第五步:更新验证
config/auth.php
<?php
return [
'defaults' => [
// 这里修改为 api
'guard' => env('AUTH_GUARD', 'api'),
'passwords' => env('AUTH_PASSWORD_BROKER', 'users'),
],
'guards' => [
'web' => [
'driver' => 'session',
'provider' => 'users',
],
'api' => [
'driver' => 'jwt',
'provider' => 'users',
],
],
...
];
第六步:用户模型实现 JWT
这一步很重要,需要在 User.php
模型文件中实现 Tymon\JWTAuth\Contracts\JWTSubject
的 getJWTIdentifier()
和 getJWTCustomClaims()
方法。
app/Models/User.php
<?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;
use Ramsey\Uuid\Exception\UnableToBuildUuidException;
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',
];
}
/**
* 获取 JWT 标识符。
*
* @return mixed
*/
public function getJWTIdentifier(): mixed
{
return $this->getKey();
}
/**
* 获取 JWT 自定义声明。
*
* @return array
*/
public function getJWTCustomClaims(): array
{
return [];
}
}
第七步:创建路由
routes/api.php
<?php
use App\Http\Controllers\Api\AuthController;
use Illuminate\Http\Request;
use Illuminate\Support\Facades\Route;
Route::group([
'middleware' => 'api',
'prefix'=>'auth',
], function () {
Route::post('register', [AuthController::class, 'register']);
Route::post('/login', [AuthController::class, 'login']);
Route::post('/logout', [AuthController::class, 'logout'])->middleware('auth:api');
Route::post('/refresh', [AuthController::class, 'refresh'])->middleware('auth:api');
Route::post('/profile', [AuthController::class, 'profile'])->middleware('auth:api');
});
第八步:功能实现
app/Http/Controllers/Api/BaseController.php
<?php
namespace App\Http\Controllers\Api;
use App\Http\Controllers\Controller;
use Illuminate\Http\JsonResponse;
class BaseController extends Controller
{
/**
* 发送成功的响应。
*
* @param mixed $result 成功数据
* @param string $message 成功消息
*
* @return JsonResponse
*/
public function sendResponse(mixed $result,string $message): JsonResponse
{
$response = [
'success' => true,
'data' => $result,
'message' => $message,
];
return response()->json($response, 200);
}
/**
* 发送错误的响应。
*
* @param string $error 错误消息
* @param mixed $errorMessages 错误详情
* @param int $code HTTP 状态码,默认为 404
*
* @return JsonResponse
*/
public function sendError(string $error, mixed $errorMessages = [], int $code = 404): JsonResponse
{
$response = [
'success' => false,
'message' => $error,
];
if (!empty($errorMessages)) {
$response['data'] = $errorMessages;
}
return response()->json($response, $code);
}
}
app/Http/Controllers/Api/AuthController.php
<?php
namespace App\Http\Controllers\Api;
use Illuminate\Http\JsonResponse;
use Illuminate\Support\Facades\Auth;
use Illuminate\Support\Facades\Validator;
use App\Models\User;
use Illuminate\Http\Request;
use Symfony\Component\HttpKernel\HttpCache\ResponseCacheStrategyInterface;
class AuthController extends BaseController
{
public function register(Request $request): JsonResponse
{
$validator = Validator::make($request->all(), [
'name' => 'required',
'email' => 'required|email',
'password' => 'required',
'c_password' => 'required|same:password',
]);
if ($validator->fails()) {
return $this->sendError('验证错误:', $validator->errors());
}
$data = $request->input();
$data['password'] = bcrypt($data['password']);
$user = User::create($data);
$success['user'] = $user;
return $this->sendResponse($success, '用户创建成功');
}
public function login(Request $request)
{
$credentials = $request->validate([
'email' => ['required', 'email'],
'password' => ['required'],
]);
if (!$token = Auth::attempt($credentials)) {
return $this->sendError('未授权:', ['error' => '未授权'], 401);
}
$success = $this->respondWithToken($token);
return $this->sendResponse($success, '用户登录成功');
}
public function profile(): JsonResponse
{
$success = auth()->user();
return $this->sendResponse($success, '获取用户资料成功');
}
public function logout(): JsonResponse
{
auth()->logout();
return $this->sendResponse([], '退出成功');
}
public function refresh(): JsonResponse
{
$success = $this->respondWithToken(auth()->refresh());
return $this->sendResponse($success, '刷新令牌成功');
}
/**
* 生成带有令牌的响应数据
*
* @param string $token 令牌
* @return array 响应数据
*/
public function respondWithToken(string $token): array
{
return [
'access_token' => $token, // 令牌
'token_type' => 'bearer', // 令牌类型
'expires_in' => auth()->factory()->getTTL() * 60 // 令牌过期时间(秒)
];
}
}
第九步:启动服务
$ php artisan serve
在需要授权验证的接口中需要添加如下请求头信息:
'headers' => [
'Accept' => 'application/json',
'Authorization' => 'Bearer '.$accessToken,
]
第十步:测试接口
我使用 Postman 进行测试,请填写好相数据进行测试,
1)用户注册:http://127.0.0.1:8000/api/auth/register
{
"success": true,
"data": {
"user": {
"name": "admin",
"email": "admin@qq.com",
"updated_at": "2024-11-20T12:31:33.000000Z",
"created_at": "2024-11-20T12:31:33.000000Z",
"id": 1
}
},
"message": "用户创建成功"
}
2)用户登录:http://127.0.0.1:8000/api/auth/login
{
"success": true,
"data": {
"access_token": "eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJpc3MiOiJodHRwOi8vbGExMXN0dWR5LnRlc3QvYXBpL2F1dGgvbG9naW4iLCJpYXQiOjE3MzIxMDY1ODUsImV4cCI6MTczMjExMDE4NSwibmJmIjoxNzMyMTA2NTg1LCJqdGkiOiI2UXQ4N1JLdWJZQjk4bGpHIiwic3ViIjoiMSIsInBydiI6IjIzYmQ1Yzg5NDlmNjAwYWRiMzllNzAxYzQwMDg3MmRiN2E1OTc2ZjcifQ.H0ksZmd1xeXsrYS31-zQUg078peh86ZNLS4FrQFtSZo",
"token_type": "bearer",
"expires_in": 3600
},
"message": "用户登录成功"
}
3)获取用户信息:http://127.0.0.1:8000/api/auth/profile
{
"success": true,
"data": {
"id": 1,
"name": "admin",
"email": "admin@qq.com",
"email_verified_at": null,
"created_at": "2024-11-20T12:31:33.000000Z",
"updated_at": "2024-11-20T12:31:33.000000Z"
},
"message": "获取用户资料成功"
}
修改一个错误的 access_token
进行测试,结果如下:
{
"message": "Unauthenticated."
}
4)刷新 token:http://127.0.0.1:8000/api/auth/refresh
{
"success": true,
"data": {
"access_token": "eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJpc3MiOiJodHRwOi8vbGExMXN0dWR5LnRlc3QvYXBpL2F1dGgvcmVmcmVzaCIsImlhdCI6MTczMjEwNjU4NSwiZXhwIjoxNzMyMTEyNjYxLCJuYmYiOjE3MzIxMDkwNjEsImp0aSI6IkxpZ0F0M3pwbHAyWVM1WlUiLCJzdWIiOiIxIiwicHJ2IjoiMjNiZDVjODk0OWY2MDBhZGIzOWU3MDFjNDAwODcyZGI3YTU5NzZmNyJ9.JK29o6KOefcEnHZzCF5f9m2h49vFG0TG4I0ogDxCPzY",
"token_type": "bearer",
"expires_in": 3600
},
"message": "刷新令牌成功"
}
4)退出登录:http://127.0.0.1:8000/api/auth/logout
{
"success": true,
"data": [],
"message": "退出成功"
}
请登录后再评论