Laravel 中 Tymon\JWTAuth 的用法
1. 安装配置
1.1 安装包
bash
composer require tymon/jwt-auth
1.2 发布配置
bash
php artisan vendor:publish --provider="Tymon\JWTAuth\Providers\LaravelServiceProvider"
1.3 生成密钥
bash
php artisan jwt:secret
1.4 配置模型
修改 config/jwt.php(可选)和用户模型:
php
// app/Models/User.php
namespace App\Models;
use Illuminate\Foundation\Auth\User as Authenticatable;
use Tymon\JWTAuth\Contracts\JWTSubject;
class User extends Authenticatable implements JWTSubject
{
// ... 其他代码
public function getJWTIdentifier()
{
return $this->getKey();
}
public function getJWTCustomClaims()
{
return [];
}
}
2. 基本使用
2.1 控制器示例
php
// app/Http/Controllers/AuthController.php
namespace App\Http\Controllers;
use App\Models\User;
use Illuminate\Http\Request;
use Illuminate\Support\Facades\Auth;
use Tymon\JWTAuth\Facades\JWTAuth;
class AuthController extends Controller
{
/**
* 用户注册
*/
public function register(Request $request)
{
$validated = $request->validate([
'name' => 'required|string|max:255',
'email' => 'required|string|email|max:255|unique:users',
'password' => 'required|string|min:6|confirmed',
]);
$user = User::create([
'name' => $validated['name'],
'email' => $validated['email'],
'password' => bcrypt($validated['password']),
]);
$token = JWTAuth::fromUser($user);
return response()->json([
'user' => $user,
'access_token' => $token,
'token_type' => 'bearer',
'expires_in' => auth()->factory()->getTTL() * 60
], 201);
}
/**
* 用户登录
*/
public function login(Request $request)
{
$credentials = $request->only('email', 'password');
if (!$token = auth()->attempt($credentials)) {
return response()->json(['error' => 'Unauthorized'], 401);
}
return $this->respondWithToken($token);
}
/**
* 获取当前用户信息
*/
public function me()
{
return response()->json(auth()->user());
}
/**
* 刷新令牌
*/
public function refresh()
{
return $this->respondWithToken(auth()->refresh());
}
/**
* 用户注销
*/
public function logout()
{
auth()->logout();
return response()->json(['message' => 'Successfully logged out']);
}
/**
* 格式化令牌响应
*/
protected function respondWithToken($token)
{
return response()->json([
'access_token' => $token,
'token_type' => 'bearer',
'expires_in' => auth()->factory()->getTTL() * 60
]);
}
}
2.2 路由设置
php
// routes/api.php
use App\Http\Controllers\AuthController;
Route::prefix('auth')->group(function () {
Route::post('register', [AuthController::class, 'register']);
Route::post('login', [AuthController::class, 'login']);
Route::middleware('auth:api')->group(function () {
Route::post('logout', [AuthController::class, 'logout']);
Route::post('refresh', [AuthController::class, 'refresh']);
Route::get('me', [AuthController::class, 'me']);
});
});
3. 中间件使用
3.1 在路由中使用
php
Route::middleware(['auth:api'])->group(function () {
Route::get('profile', 'ProfileController@show');
Route::put('profile', 'ProfileController@update');
});
3.2 自定义中间件
php
// app/Http/Middleware/JwtMiddleware.php
namespace App\Http\Middleware;
use Closure;
use Tymon\JWTAuth\Facades\JWTAuth;
class JwtMiddleware
{
public function handle($request, Closure $next)
{
try {
$user = JWTAuth::parseToken()->authenticate();
} catch (\Exception $e) {
if ($e instanceof \Tymon\JWTAuth\Exceptions\TokenInvalidException) {
return response()->json(['error' => 'Token is invalid'], 401);
} else if ($e instanceof \Tymon\JWTAuth\Exceptions\TokenExpiredException) {
return response()->json(['error' => 'Token is expired'], 401);
} else {
return response()->json(['error' => 'Authorization Token not found'], 401);
}
}
return $next($request);
}
}
4. 高级用法
4.1 自定义声明
php
// 在模型中
public function getJWTCustomClaims()
{
return [
'role' => $this->role,
'permissions' => $this->permissions()->pluck('name')->toArray()
];
}
// 创建令牌时添加自定义声明
$customClaims = ['foo' => 'bar', 'baz' => 'bob'];
$token = JWTAuth::claims($customClaims)->attempt($credentials);
4.2 设置令牌有效期
php
// 临时设置令牌有效期
$token = auth()->setTTL(7200)->attempt($credentials);
// 永久设置(在 .env 文件中)
JWT_TTL=60
JWT_REFRESH_TTL=20160
4.3 刷新令牌机制
php
// 配置刷新令牌过期时间(config/jwt.php)
'refresh_ttl' => 20160, // 2周
// 使用刷新令牌
try {
$newToken = JWTAuth::refresh(JWTAuth::getToken());
} catch (TokenBlacklistedException $e) {
return response()->json(['error' => 'Token has been blacklisted'], 401);
}
4.4 黑名单和注销
php
// 将令牌加入黑名单
JWTAuth::invalidate(JWTAuth::getToken());
// 检查令牌是否在黑名单中
if (JWTAuth::parseToken()->check()) {
// 令牌有效
}
5. 异常处理
5.1 全局异常处理
php
// app/Exceptions/Handler.php
public function register()
{
$this->renderable(function (\Tymon\JWTAuth\Exceptions\TokenExpiredException $e) {
return response()->json(['error' => 'Token expired'], 401);
});
$this->renderable(function (\Tymon\JWTAuth\Exceptions\TokenInvalidException $e) {
return response()->json(['error' => 'Token invalid'], 401);
});
$this->renderable(function (\Tymon\JWTAuth\Exceptions\JWTException $e) {
return response()->json(['error' => 'Token absent'], 401);
});
}
6. 测试示例
php
// tests/Feature/AuthTest.php
namespace Tests\Feature;
use App\Models\User;
use Illuminate\Foundation\Testing\RefreshDatabase;
use Tests\TestCase;
class AuthTest extends TestCase
{
use RefreshDatabase;
public function test_login_successful()
{
$user = User::factory()->create([
'password' => bcrypt('password123')
]);
$response = $this->postJson('/api/auth/login', [
'email' => $user->email,
'password' => 'password123'
]);
$response->assertStatus(200)
->assertJsonStructure([
'access_token',
'token_type',
'expires_in'
]);
}
public function test_protected_route_access()
{
$user = User::factory()->create();
$token = auth()->login($user);
$response = $this->withHeaders([
'Authorization' => 'Bearer ' . $token
])->getJson('/api/auth/me');
$response->assertStatus(200)
->assertJson([
'email' => $user->email
]);
}
}
7. 前端使用示例
javascript
// Vue.js 示例
import axios from 'axios';
// 设置 axios 默认配置
axios.defaults.baseURL = 'http://api.example.com';
// 请求拦截器:添加 token
axios.interceptors.request.use(config => {
const token = localStorage.getItem('access_token');
if (token) {
config.headers.Authorization = `Bearer ${token}`;
}
return config;
});
// 响应拦截器:处理 token 过期
axios.interceptors.response.use(
response => response,
async error => {
const originalRequest = error.config;
if (error.response.status === 401 && !originalRequest._retry) {
originalRequest._retry = true;
try {
const refreshToken = localStorage.getItem('refresh_token');
const response = await axios.post('/auth/refresh', {
refresh_token: refreshToken
});
localStorage.setItem('access_token', response.data.access_token);
originalRequest.headers.Authorization = `Bearer ${response.data.access_token}`;
return axios(originalRequest);
} catch (refreshError) {
// 刷新 token 失败,跳转到登录页
localStorage.removeItem('access_token');
localStorage.removeItem('refresh_token');
window.location.href = '/login';
return Promise.reject(refreshError);
}
}
return Promise.reject(error);
}
);
8. 安全建议
- 使用 HTTPS:始终在生产环境中使用 HTTPS
- 设置合理的过期时间:访问令牌设置较短时间,刷新令牌设置较长时间
- 黑名单管理:注销时及时将令牌加入黑名单
- 限制刷新次数:防止刷新令牌被滥用
- 存储安全:前端安全存储令牌(避免 XSS 攻击)
这个指南涵盖了 Tymon/JWTAuth 的主要用法,你可以根据项目需求进行调整和扩展。