Laravel中Tymon\JWTAuth 的用法示例

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. 安全建议

  1. 使用 HTTPS:始终在生产环境中使用 HTTPS
  2. 设置合理的过期时间:访问令牌设置较短时间,刷新令牌设置较长时间
  3. 黑名单管理:注销时及时将令牌加入黑名单
  4. 限制刷新次数:防止刷新令牌被滥用
  5. 存储安全:前端安全存储令牌(避免 XSS 攻击)

这个指南涵盖了 Tymon/JWTAuth 的主要用法,你可以根据项目需求进行调整和扩展。

相关推荐
BingoGo13 小时前
当你的 PHP 应用的 API 没有限流时会发生什么?
后端·php
JaguarJack13 小时前
当你的 PHP 应用的 API 没有限流时会发生什么?
后端·php·服务端
BingoGo2 天前
OpenSwoole 26.2.0 发布:支持 PHP 8.5、io_uring 后端及协程调试改进
后端·php
JaguarJack2 天前
OpenSwoole 26.2.0 发布:支持 PHP 8.5、io_uring 后端及协程调试改进
后端·php·服务端
JaguarJack2 天前
推荐 PHP 属性(Attributes) 简洁读取 API 扩展包
后端·php·服务端
BingoGo2 天前
推荐 PHP 属性(Attributes) 简洁读取 API 扩展包
php
BingoGo4 天前
告别 Laravel 缓慢的 Blade!Livewire Blaze 来了,为你的 Laravel 性能提速
后端·laravel
JaguarJack4 天前
告别 Laravel 缓慢的 Blade!Livewire Blaze 来了,为你的 Laravel 性能提速
后端·php·laravel
郑州光合科技余经理4 天前
代码展示:PHP搭建海外版外卖系统源码解析
java·开发语言·前端·后端·系统架构·uni-app·php
QQ5110082854 天前
python+springboot+django/flask的校园资料分享系统
spring boot·python·django·flask·node.js·php