您的当前位置:首页>全部文章>文章详情
Thinkphp6集成JWT
发表于:2021-12-24 15:41:00浏览:1391次TAG: #ThinkPHP #JWT

JWT 全称 Json Web token,是为了在网络应用环境间传递声明而执行的一种基于json的开放标准(RFC 7519),该token被设计为紧凑且安全的,特别适用于分布式站点的单点登录(SSO)场景。JWT的声明一般被用来在身份提供者和服务提供者之间传递被认证的用户身份信息,以便于从服务器获取资源,也可以增加一些额外的其他业务逻辑所必须的声明信息,该token也可之间被用于认证,也可以被加密。

PHP有很多JWT包,这里主要介绍使用lcobucci/jwt包。

安装:

composer require lcobucci/jwt

封装:

文件:\app\api\service\JwtAuth.php

<?php
namespace app\api\service;
use Lcobucci\JWT\Builder;
use Lcobucci\JWT\Parser;
use Lcobucci\JWT\Signer\Hmac\Sha256;
use Lcobucci\JWT\ValidationData;
/**
 * 单例 一次请求中所有出现jwt的地方都是一个用户
 * Class JwtAuth
 * @package app\api\service
 */
class JwtAuth
{
    // jwt token
    private $token;
    // jwt 过期时间
    private $expTime = 3600;
    // claim iss 签发组织
    private $iss = 'api.gougucms.com';
    // claim aud签发作者
    private $aud = 'gougucms';
    // claim uid
    private $uid;
    // secrect
    private $secrect = 'GOUGUCMS';
    // decode token
    private $decodeToken;
    // 单例模式JwtAuth句柄
    private static $instance;
    // 获取JwtAuth的句柄
    public static function getInstance()
    {
        if (is_null(self::$instance)) {
            self::$instance = new self();
        }
        return self::$instance;
    }
    // 私有化构造函数
    private function __construct()
    {
    }
    // 私有化clone函数
    private function __clone()
    {
        // TODO: Implement __clone() method.
    }
    // 获取token
    public function getToken()
    {
        return (string)$this->token;
    }
    // 设置token
    public function setToken($token)
    {
        $this->token = $token;
        return $this;
    }
    // 设置uid
    public function setUid($uid)
    {
        $this->uid = $uid;
        return $this;
    }
    // 获取uid
    public function getUid()
    {
        return $this->uid;
    }
    // 编码jwt token
    public function encode()
    {
        $time = time();//签发时间
        $this->token = (new Builder())->setHeader('alg', 'HS256')
            ->setIssuer($this->iss)
            ->setAudience($this->aud)
            ->setIssuedAt($time)
            ->setExpiration($time + $this->expTime)
            ->set('uid', $this->uid)
            ->sign(new Sha256(), $this->secrect)
            ->getToken();
        return $this;
    }
    public function decode()
    {
        if (!$this->decodeToken) {
            $this->decodeToken = (new Parser())->parse((string)$this->token); // Parses from a string
            $this->uid = $this->decodeToken->getClaim('uid');
        }
        return $this->decodeToken;
    }
    // validate
    public function validate()
    {
        $data = new ValidationData(); // It will use the current time to validate (iat, nbf and exp)
        $data->setIssuer($this->iss);
        $data->setAudience($this->aud);
        $data->setId($this->uid);
        return $this->decode()->validate($data);
    }
    // verify token
    public function verify()
    {
        $signer = new Sha256();
        return $this->decode()->verify($signer, $this->secrect);
    }
}

使用:

中间组件文件:\app\api\middleware\Api.php

<?php
/**
 * +----------------------------------------------------------------------
 * | Api中间件
 * +----------------------------------------------------------------------
 */
namespace app\api\middleware;
use app\api\service\JwtAuth;
use think\facade\Request;
use think\Response;
use think\exception\HttpResponseException;
class Api
{
    public function handle($request, \Closure $next)
    {
        $token = Request::header('token');
        if ($token) {
            if (count(explode('.', $token)) <> 3) {
                $this->result([], 110, 'token格式错误');
            }
            $jwtAuth = JwtAuth::getInstance();
            $jwtAuth->setToken($token);
            if ($jwtAuth->validate() && $jwtAuth->verify()) {
                return $next($request);
            } else {
                $this->result([], 111, 'token已过期');
            }
        } else {
            $this->result([], 112, 'token不能为空');
        }
        return $next($request);
    }
    /**
     * 返回封装后的API数据到客户端
     * @param  mixed   $data 要返回的数据
     * @param  integer $code 返回的code
     * @param  mixed   $msg 提示信息
     * @param  string  $type 返回数据格式
     * @param  array   $header 发送的Header信息
     * @return Response
     */
    protected function result($data, int $code = 0, $msg = '', string $type = '', array $header = []): Response
    {
        $result = [
            'code' => $code,
            'msg'  => $msg,
            'time' => time(),
            'data' => $data,
        ];
        $type     = $type ?: 'json';
        $response = Response::create($result, $type)->header($header);
        throw new HttpResponseException($response);
    }
}

调用:

文件:\app\api\controller\Index.php

<?php
/**
 * +----------------------------------------------------------------------
 * | api控制器
 * +----------------------------------------------------------------------
 */
namespace app\api\controller;
use app\api\service\JwtAuth;
use think\facade\Db;
use think\facade\Request;
class Index extends Base
{
    /**
     * 控制器中间件 [登录、注册 不需要鉴权]
     * @var array
     */
    protected $middleware = [
        'app\api\middleware\Api' => ['except' => ['login', 'register']],
    ];
    /**
     * @api {post} /User/login 01、会员登录
     * @apiDescription 系统登录接口,返回 token 用于操作需验证身份的接口
     * @apiParam (请求参数:) {string}             username 登录用户名
     * @apiParam (请求参数:) {string}             password 登录密码
     * @apiParam (响应字段:) {string}             token    Token
     * @apiSuccessExample {json} 成功示例
     * {"code":1,"msg":"登录成功","time":1563525780,"data":{"token":"eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJpc3MiOiJhcGkuc2l5dWNtcy5jb20iLCJhdWQiOiJzaXl1Y21zX2FwcCIsImlhdCI6MTU2MzUyNTc4MCwiZXhwIjoxNTYzNTI5MzgwLCJ1aWQiOjEzfQ.prQbqT00DEUbvsA5M14HpNoUqm31aj2JEaWD7ilqXjw"}}
     * @apiErrorExample {json} 失败示例
     * {"code":0,"msg":"帐号或密码错误","time":1563525638,"data":[]}
     */
    public function login(string $username, string $password)
    {
        // 校验用户名密码
        $user = Users::where('email|mobile', $username)
            ->where('password', md5($password))
            ->find();
        if (empty($user)) {
            $this->result([], 0, '帐号或密码错误');
        } else {
            if ($user['status'] == 1) {
                //获取jwt的句柄
                $jwtAuth = JwtAuth::getInstance();
                $token = $jwtAuth->setUid($user['id'])->encode()->getToken();
                //更新信息
                Users::where('id', $user['id'])
                    ->update(['last_login_time' => time(), 'last_login_ip' => Request::ip()]);
                $this->result(['token' => $token], 1, '登录成功');
            } else {
                $this->result([], 0, '用户已被禁用');
            }
        }
    }
}