ThinkPHP5集成JWT Token实现API接口认证全流程

键盘旁的猫
键盘旁的猫
发布时间:2026-10-13| 4人浏览过
原创

在构建基于ThinkPHP5的RESTful API或前后端分离项目时,传统的Session/Cookie认证往往面临跨域和分布式扩展难题。JWT(JSON Web Token)作为一种开放标准,能够实现无状态的身份验证。本文将演示如何利用Lcobucci/jwt库,在ThinkPHP5中实现从Token生成、配置到自动验证的全流程代码。

准备条件:环境配置与依赖安装

首先,确保你的开发环境中已配置好 Composer。为了加速依赖包的下载,建议先将镜像源切换为阿里云镜像:

composer config -g repo.packagist composer https://mirrors.aliyun.com/composer/

接着,在 ThinkPHP5 项目根目录下,通过 Composer 安装 JWT 库。这里推荐使用稳定版本 3.3:

composer require lcobucci/jwt:3.3

第一步:创建 Token 辅助类

为了方便调用,建议在项目的 extend 目录下新建一个自定义类文件,例如 Token.php。该类将封装 Token 的生成与验证逻辑,避免在业务控制器中耦合过多底层代码。

在 Token.php 中,引入必要的命名空间并定义 createToken 静态方法。该方法负责组装 JWT 的各个部分,包括发行者、受众、唯一标识符、签发时间、生效时间及过期时间等。

<?php
namespace extend;

use Lcobucci\JWT\Builder;
use Lcobucci\JWT\signer\Hmac\Sha256;

class Token
{
    /**
     * 生成 JWT Token
     * @param string|null $uid 用户ID
     * @return string
     */
    public static function createToken($uid = null)
    {
        // 1. 设置签名算法 (HS256 需要 Secret Key)
        $signer = new Sha256('your-secret-key-here');
        
        // 2. 获取当前时间戳
        $time = time();
        
        // 3. 构建 Token 内容
        $token = (new Builder())
            ->issuedBy('my-app')           // Issuer (发行人)
            ->canonlyBeUsedBy('api-users') // Audience (受众/接收人)
            ->identifiedBy('unique-id')    // JTI (JWT ID)
            ->issuedAt($time)              // IAT (Issued At)
            ->canonlyBeUsedAfter($time)    // NBF (Not Before) - 立即生效
            ->expiresAt($time + 3600)      // EXP (Expiration) - 1小时后过期
            ->with('uid', $uid);           // 自定义 Payload,存放用户ID
            
        // 4. 生成签名并返回字符串形式的 Token
        return (string)$token->sign($signer)->getToken();
    }
}

注意:在实际项目中,'your-secret-key-here' 应当作为敏感信息存储在配置文件(如 config/app.php 或环境变量)中,切勿硬编码或在代码仓库中泄露。此外,canonlyBeUsedAfter 用于设置令牌生效前的等待时间,通常设为即时生效即可。

第二步:在控制器中测试生成 Token

创建一个简单的控制器来测试 Token 的生成。在 app/index/controller 下新建或修改控制器:

<?php
namespace app\index\controller;

use think\Controller;
use extend\Token;

class Test extends Controller
{
    public function generate()
    {
        // 假设用户ID为 1001
        $token = Token::createToken(1001);
        echo "Generated Token: " . $token;
    }
}

访问该接口后,你将获得一串由三个点 . 分隔的 Base64url 编码字符串。这就是标准的 JWT 格式。

第三步:实现 Token 验证逻辑

仅生成 Token 是不够的,核心在于验证其有效性。我们需要在 Token.php 中增加 verifyToken 方法。该方法需要重新构建相同的签名算法和配置参数,以对比传入的 Token 是否被篡改或过期。

在 Token.php 中添加以下静态方法:

// 在 extend/Token.php 中
use Lcobucci\JWT\Parser;
use Lcobucci\JWT\ValidationData;
use Lcobucci\JWT\signer\Hmac\Sha256;

// ... (前面的 createToken 方法保持不变)

/**
 * 验证 Token 的有效性
 * @param string $token
 * @return mixed 验证失败返回 false,成功则返回 payload 中的 uid
 */
public static function verifyToken($token)
{
    if (empty($token)) {
        return false;
    }

    try {
        // 1. 解析 Token
        $parser = new Parser();
        $tokenObj = $parser->parse((string) $token);

        // 2. 设置验证数据 (必须与生成时一致)
        $data = new ValidationData();
        $data->setIssuer('my-app');       // 发行人
        $data->setAudience('api-users');   // 受众
        $data->setId('unique-id');         // ID
        $data->setIssuedAtRange(time() - 5, time() + 5); // 允许的时间偏差 (可选,应对服务器时间差异)

        // 3. 验证声明 (Claims)
        if (!$tokenObj->validate($data)) {
            return false;
        }

        // 4. 验证签名 (Signature)
        $signer = new Sha256('your-secret-key-here');
        if (!$tokenObj->verify($signer, 'your-secret-key-here')) {
            return false;
        }

        // 5. 验证通过,提取载荷中的用户ID
        return $tokenObj->getClaim('uid');

    } catch (\Exception $e) {
        // 捕获解析异常
        return false;
    }
}

第四步:实战应用——登录鉴权流程

以下是一个完整的登录接口实现示例。展示如何在校验用户名密码后签发 Token,并在后续请求中利用 BaseController 自动验证 Token。

1. 登录接口 (Login Controller)

在此处不推荐直接使用原生 DB,建议使用 Model。此处仅为原理演示:

<?php
namespace app\index\controller;

use think\Controller;
use think\Db;
use extend\Token;

class Login extends Controller
{
    public function doLogin()
    {
        $input = input();
        $username = isset($input['username']) ? trim($input['username']) : '';
        $password = isset($input['password']) ? trim($input['password']) : '';

        if (empty($username) || empty($password)) {
            return json(['code' => 400, 'msg' => '账号或密码不能为空']);
        }

        // 简单MD5加密 (生产环境建议加盐或使用 bcrypt)
        $password = md5($password);

        // 查询用户
        $user = Db::name('users')->where(['username' => $username, 'password' => $password])->find();

        if (!$user) {
            return json(['code' => 401, 'msg' => '账号或密码错误']);
        }

        // 生成 Token
        $token = Token::createToken($user['id']);
        
        // 移除敏感信息 (如 password)
        unset($user['password']);
        $user['token'] = $token;

        return json(['code' => 200, 'msg' => '登录成功', 'data' => $user]);
    }
}

2. 受保护接口与基础控制器自动验证

为了让所有需要认证的接口共享验证逻辑,创建一个 Base 控制器,在其他业务控制器中继承它。

<?php
namespace app\index\controller;

use think\Controller;
use think\Request;
use extend\Token;

class Base extends Controller
{
    public function _initialize(Request $request = null)
    {
        parent::_initialize();
        
        // 获取前端传递的 Token (通常在 Header 中,这里模拟从参数获取)
        $token = $request->param('token', '');
        if (empty($token)) {
            // 尝试从 Header 获取
            $token = $request->header('Authorization');
            // 如果是 Bearer Token 格式,截取后半部分
            if ($token && strpos($token, 'Bearer ') === 0) {
                $token = substr($token, 7);
            }
        }

        // 验证 Token
        $userId = Token::verifyToken($token);
        
        if (!$userId) {
            exit(json_encode([
                'code' => 403,
                'msg' => '无效的 Token 或未授权访问'
            ], JSON_UNESCAPED_UNICODE));
        }

        // 验证通过后,将用户ID存入全局变量或Request对象
        $this->assignAuthUser($userId);
    }
}

随后,其他受保护的接口只需继承 Base 类即可自动完成认证:

<?php
namespace app\index\controller;

class Order extends Base 
{
    public function getList()
    {
        // 由于继承了 Base,此处 token 已在 _initialize 中被验证
        // 可以直接使用 $this->authUserId 获取当前用户ID
        $userId = session::get('current_uid'); // 假设你在 Base 中存入了 session
        
        $orders = Db::name('orders')->where('user_id', $userId)->select();
        return json(['code' => 200, 'data' => $orders]);
    }
}

总结与安全建议

通过上述步骤,我们完成了在 ThinkPHP5 中基于 JWT 的 Token 认证闭环。关键点在于:
1. 保持生成与验证时的配置(Issuer, Audience, Signer, Key)完全一致。
2. 使用安全的密钥(Secret Key),严禁使用弱口令。
3. 合理设置过期时间(Expiration),对于高安全性要求的服务,可配合 Refresh Token 机制使用。
4. Token 一旦签发无法在服务端主动“注销”,除非引入黑名单机制或使用较短的生命周期。

以上就是在 ThinkPHP5 中实现 JWT Token 认证的详细内容,更多关于 PHP API 安全开发的资料请关注本站其它相关文章!

相关标签:

免责声明:本文内容仅供参考,不代表本站观点。转载或引用请注明出处。

Python 相关产品和应用

全部下载
Selenium

Selenium

4.51.0 · Windows

用于浏览器自动化和 Web 测试的 Python 工具。

scikit-learn

scikit-learn

1.9.1 · Windows

常用 Python 机器学习算法与模型评估工具库。

Black

Black

26.10.1 · Windows

强调一致性的 Python 自动代码格式化工具。

Ruff

Ruff

0.17.1 · macOS

由 Rust 编写的高速 Python 代码检查和格式化工具。

pytest

pytest

9.1.1 · Windows

简洁而强大的 Python 自动化测试框架。

FastAPI

FastAPI

0.146.0 · Windows

高性能 Python API 框架,支持类型提示和自动接口文档。

Flask

Flask

3.1.3 · Windows

轻量灵活的 Python Web 应用框架。

Django

Django

6.1.2 · Windows

成熟的 Python Web 框架,适合构建内容站点和业务系统。

Spyder

Spyder

6.1.7 · Windows

面向科学计算与数据分析的 Python 集成开发环境。

SciPy

SciPy

1.18.1 · Windows

面向科学、工程和技术计算的 Python 算法库。