本文目录导读:

我来详细介绍在PHP项目中实现Token管理的几种常见方案。
使用JWT(JSON Web Token)方案
1 安装JWT库
composer require firebase/php-jwt
2 创建Token管理类
<?php
namespace App\Services;
use Firebase\JWT\JWT;
use Firebase\JWT\Key;
use Firebase\JWT\ExpiredException;
class TokenService
{
private string $secretKey;
private string $algorithm;
private int $accessTokenExpiration;
private int $refreshTokenExpiration;
public function __construct()
{
$this->secretKey = getenv('JWT_SECRET') ?: 'your-secret-key-here';
$this->algorithm = 'HS256';
$this->accessTokenExpiration = 3600; // 1小时
$this->refreshTokenExpiration = 604800; // 7天
}
/**
* 生成访问令牌
*/
public function generateAccessToken(array $payload): string
{
$tokenPayload = [
'iss' => 'your-app-name', // 签发者
'iat' => time(), // 签发时间
'exp' => time() + $this->accessTokenExpiration, // 过期时间
'sub' => $payload['user_id'],
'role' => $payload['role'] ?? 'user',
'type' => 'access'
];
return JWT::encode($tokenPayload, $this->secretKey, $this->algorithm);
}
/**
* 生成刷新令牌
*/
public function generateRefreshToken(array $payload): string
{
$tokenPayload = [
'iss' => 'your-app-name',
'iat' => time(),
'exp' => time() + $this->refreshTokenExpiration,
'sub' => $payload['user_id'],
'token_id' => uniqid('refresh_', true),
'type' => 'refresh'
];
return JWT::encode($tokenPayload, $this->secretKey, $this->algorithm);
}
/**
* 验证令牌
*/
public function validateToken(string $token): ?object
{
try {
$decoded = JWT::decode($token, new Key($this->secretKey, $this->algorithm));
return $decoded;
} catch (ExpiredException $e) {
// 令牌过期
return null;
} catch (\Exception $e) {
// 其他验证错误
return null;
}
}
}
使用框架内置功能(以Laravel为例)
1 Laravel Sanctum(适合API)
<?php
namespace App\Http\Controllers\Api;
use App\Models\User;
use Illuminate\Http\Request;
use Illuminate\Support\Facades\Hash;
use Illuminate\Validation\ValidationException;
class AuthController extends Controller
{
/**
* 用户登录并生成令牌
*/
public function login(Request $request)
{
$request->validate([
'email' => 'required|email',
'password' => 'required',
]);
$user = User::where('email', $request->email)->first();
if (!$user || !Hash::check($request->password, $user->password)) {
throw ValidationException::withMessages([
'email' => ['提供的凭据不正确。'],
]);
}
// 创建令牌
$token = $user->createToken('api-token')->plainTextToken;
return response()->json([
'access_token' => $token,
'token_type' => 'Bearer',
]);
}
/**
* 验证用户令牌
*/
public function user(Request $request)
{
return $request->user();
}
/**
* 撤销当前令牌
*/
public function logout(Request $request)
{
$request->user()->currentAccessToken()->delete();
return response()->json(['message' => '已成功注销']);
}
}
2 路由配置
// routes/api.php
use App\Http\Controllers\Api\AuthController;
Route::post('/login', [AuthController::class, 'login']);
Route::middleware('auth:sanctum')->group(function () {
Route::get('/user', [AuthController::class, 'user']);
Route::post('/logout', [AuthController::class, 'logout']);
});
简单Token实现(不依赖库)
<?php
namespace App\Services;
class SimpleTokenService
{
private string $secretKey;
private int $expiration;
public function __construct()
{
$this->secretKey = 'your-secret-key';
$this->expiration = 3600; // 1小时
}
/**
* 生成简单令牌
*/
public function generateToken(array $data): string
{
$payload = base64_encode(json_encode([
'data' => $data,
'exp' => time() + $this->expiration,
'iat' => time()
]));
$signature = hash_hmac('sha256', $payload, $this->secretKey);
return base64_encode($payload . '.' . $signature);
}
/**
* 验证简单令牌
*/
public function validateToken(string $token): ?array
{
try {
$decoded = base64_decode($token);
$parts = explode('.', $decoded);
if (count($parts) !== 2) {
return null;
}
[$payload, $signature] = $parts;
// 验证签名
$expectedSignature = hash_hmac('sha256', $payload, $this->secretKey);
if (!hash_equals($expectedSignature, $signature)) {
return null;
}
// 解码载荷
$data = json_decode(base64_decode($payload), true);
// 检查过期时间
if ($data['exp'] < time()) {
return null;
}
return $data['data'];
} catch (\Exception $e) {
return null;
}
}
}
中间件实现
1 认证中间件
<?php
namespace App\Http\Middleware;
use App\Services\TokenService;
use Closure;
use Illuminate\Http\Request;
class TokenAuthMiddleware
{
private TokenService $tokenService;
public function __construct(TokenService $tokenService)
{
$this->tokenService = $tokenService;
}
/**
* 处理传入请求
*/
public function handle(Request $request, Closure $next)
{
$token = $request->bearerToken();
if (!$token) {
return response()->json([
'error' => '未提供令牌',
'message' => '请求已禁止'
], 401);
}
$decoded = $this->tokenService->validateToken($token);
if (!$decoded) {
return response()->json([
'error' => '无效或过期的令牌',
'message' => '请重新登录'
], 401);
}
// 将用户信息添加到请求中
$request->merge(['auth_user' => $decoded]);
return $next($request);
}
}
2 刷新令牌中间件
<?php
namespace App\Http\Middleware;
use App\Services\TokenService;
use Closure;
use Illuminate\Http\Request;
class TokenRefreshMiddleware
{
private TokenService $tokenService;
public function __construct(TokenService $tokenService)
{
$this->tokenService = $tokenService;
}
/**
* 检查令牌是否需要刷新
*/
public function handle(Request $request, Closure $next)
{
$response = $next($request);
// 如果令牌快要过期,生成新的令牌
if ($request->has('auth_user')) {
$user = $request->auth_user;
$tokenExp = $user->exp ?? 0;
$remainingTime = $tokenExp - time();
// 剩余时间少于10分钟时刷新令牌
if ($remainingTime < 600) {
$newToken = $this->tokenService->generateAccessToken([
'user_id' => $user->sub,
'role' => $user->role ?? 'user'
]);
$response->headers->set('X-New-Token', $newToken);
}
}
return $response;
}
}
完整示例:用户登录系统
1 用户模型
<?php
namespace App\Models;
use Illuminate\Database\Eloquent\Model;
class User extends Model
{
protected $fillable = [
'username',
'email',
'password',
'role'
];
protected $hidden = [
'password',
'remember_token',
];
}
2 认证控制器
<?php
namespace App\Http\Controllers\Api;
use App\Models\User;
use App\Services\TokenService;
use Illuminate\Http\Request;
use Illuminate\Support\Facades\Hash;
class AuthController extends Controller
{
private TokenService $tokenService;
public function __construct(TokenService $tokenService)
{
$this->tokenService = $tokenService;
}
/**
* 用户注册
*/
public function register(Request $request)
{
$validated = $request->validate([
'username' => 'required|string|max:255|unique:users',
'email' => 'required|string|email|max:255|unique:users',
'password' => 'required|string|min:8|confirmed',
]);
$user = User::create([
'username' => $validated['username'],
'email' => $validated['email'],
'password' => Hash::make($validated['password']),
]);
// 生成令牌
$accessToken = $this->tokenService->generateAccessToken([
'user_id' => $user->id,
'role' => 'user'
]);
$refreshToken = $this->tokenService->generateRefreshToken([
'user_id' => $user->id
]);
return response()->json([
'user' => $user,
'access_token' => $accessToken,
'refresh_token' => $refreshToken,
'token_type' => 'Bearer'
], 201);
}
/**
* 用户登录
*/
public function login(Request $request)
{
$validated = $request->validate([
'email' => 'required|email',
'password' => 'required',
]);
$user = User::where('email', $validated['email'])->first();
if (!$user || !Hash::check($validated['password'], $user->password)) {
return response()->json([
'error' => '认证失败',
'message' => '邮箱或密码错误'
], 401);
}
// 生成令牌
$accessToken = $this->tokenService->generateAccessToken([
'user_id' => $user->id,
'role' => $user->role
]);
$refreshToken = $this->tokenService->generateRefreshToken([
'user_id' => $user->id
]);
return response()->json([
'user' => $user,
'access_token' => $accessToken,
'refresh_token' => $refreshToken,
'token_type' => 'Bearer',
'expires_in' => 3600
]);
}
/**
* 刷新令牌
*/
public function refreshToken(Request $request)
{
$validated = $request->validate([
'refresh_token' => 'required|string'
]);
$decoded = $this->tokenService->validateToken($validated['refresh_token']);
if (!$decoded || $decoded->type !== 'refresh') {
return response()->json([
'error' => '无效的刷新令牌'
], 401);
}
// 生成新的访问令牌
$user = User::find($decoded->sub);
if (!$user) {
return response()->json([
'error' => '用户不存在'
], 401);
}
$newAccessToken = $this->tokenService->generateAccessToken([
'user_id' => $user->id,
'role' => $user->role
]);
return response()->json([
'access_token' => $newAccessToken,
'token_type' => 'Bearer',
'expires_in' => 3600
]);
}
}
最佳实践建议
安全性建议
- 使用HTTPS:始终在HTTPS下传输令牌
- 令牌有效期:访问令牌短时(15分钟-1小时),刷新令牌长时(7-30天)
- 密钥管理:密钥存储在环境变量中,定期更换
- 令牌撤销:实现令牌黑名单机制
性能优化
- 缓存令牌验证:使用Redis缓存已验证的令牌
- 数据库索引:为令牌相关字段创建索引
- 批量操作:需要撤销多个令牌时使用批量操作
常见问题处理
// 令牌失效处理
try {
$user = auth()->userOrFail();
} catch (\Tymon\JWTAuth\Exceptions\UserNotDefinedException $e) {
return response()->json(['error' => '用户未找到'], 401);
} catch (\Tymon\JWTAuth\Exceptions\TokenExpiredException $e) {
return response()->json(['error' => '令牌已过期'], 401);
} catch (\Tymon\JWTAuth\Exceptions\TokenInvalidException $e) {
return response()->json(['error' => '无效令牌'], 401);
}
选择哪种实现方式取决于你的项目需求:
- 小型项目:使用简单的自定义实现
- 大型企业应用:推荐使用JWT + 框架内置功能
- 微服务架构:考虑使用OAuth2.0协议
版权声明:足球直播
足球直播
足球直播
足球直播
足球直播
中超直播
中超直播
中超直播
中超直播
中超直播
英超直播
英超直播
英超直播
英超直播
英超直播
德甲直播
德甲直播
德甲直播
德甲直播
德甲直播
西甲直播
西甲直播
西甲直播
西甲直播
西甲直播
意甲直播
意甲直播
意甲直播
意甲直播
意甲直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
篮球直播
篮球直播
篮球直播
篮球直播
篮球直播
足球直播
足球直播
足球直播
足球直播
足球直播
中超直播
中超直播
中超直播
中超直播
中超直播
英超直播
英超直播
英超直播
英超直播
英超直播
德甲直播
德甲直播
德甲直播
德甲直播
德甲直播
西甲直播
西甲直播
西甲直播
西甲直播
西甲直播
意甲直播
意甲直播
意甲直播
意甲直播
意甲直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
篮球直播
篮球直播
篮球直播
篮球直播
篮球直播
足球直播
足球直播
足球直播
足球直播
足球直播
中超直播
中超直播
中超直播
中超直播
中超直播
英超直播
英超直播
英超直播
英超直播
英超直播
德甲直播
德甲直播
德甲直播
德甲直播
德甲直播
西甲直播
西甲直播
西甲直播
西甲直播
西甲直播
意甲直播
意甲直播
意甲直播
意甲直播
意甲直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
篮球直播
篮球直播
篮球直播
篮球直播
篮球直播