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 的主要用法,你可以根据项目需求进行调整和扩展。

相关推荐
三七吃山漆2 小时前
攻防世界——Web_php_wrong_nginx_config
开发语言·nginx·安全·web安全·网络安全·php·ctf
渣渣盟18 小时前
网络命令大全:轻松解决网络故障
开发语言·php
百***074518 小时前
gpt-image-1.5国内API稳定接入全方案(含多模态实操)
开发语言·gpt·php
小韩博19 小时前
小迪第42课:PHP应用&MYSQL架构&SQL注入&跨库查询&文件读写&权限操作
sql·mysql·网络安全·架构·php
天聚数行19 小时前
华为鸿蒙系统(HarmonyOS)调用天聚数行 API 教程
华为·php·harmonyos·tianapi·天聚数行
TG:@yunlaoda360 云老大20 小时前
如何在华为云国际站代理商控制台进行基础状态核查的常见问题解答
数据库·华为云·php
程序员在囧途21 小时前
Sora2 25 秒视频 API 国内直连!10 积分/次,稳定秒退任务,支持 avatar & Remix(附 PHP 接入教程)
后端·开源·php
峰顶听歌的鲸鱼1 天前
15.docker:网络
运维·网络·docker·容器·云计算·php·学习方法
catchadmin1 天前
使用 PHP 和 WebSocket 构建实时聊天应用 完整指南
开发语言·websocket·php