ThinkPhp5.0.24 Erro JWT '"garoto" vazio, não foi possível procurar a chave correta' solução

Eu encontrei um buraco hoje e gravei.
Antes da gravação, ainda é necessário reabastecer o fosso do ambiente.
Há algum tempo, gravei que não sei muito sobre o ThinkPhp5, o ambiente de desenvolvimento é o macos11, pois o macos vem com o apache e o ambiente de desenvolvimento do php, basta ligar o switch php no http.conf do arquivo de configuração do apache. Mas depois de trocar o dispositivo, o ambiente de desenvolvimento é o macos12, e o apache que vem com o sistema remove o php, então para configurar o ambiente php, além de instalar o php, adicione o caminho de execução do php no arquivo http.conf do apache, e importar módulos relacionados. Para assinar o arquivo http.con com um certificado (desisti neste ponto porque não encontrei a assinatura do certificado). Existe apenas uma maneira de alterá-lo, que é usar o brew para instalar o php, instalar o httpd e adicionar o ambiente operacional e importar os módulos ao seu arquivo de configuração (para obter detalhes, consulte este ).
Configure também o ambiente

export PATH="/usr/local/opt/[email protected]/bin:$PATH"
export PATH="/opt/homebrew/bin:$PATH"

Instale também o instalador do kit de ferramentas do composer.

brew install composer

Após a instalação, você pode criar um projeto php.

composer create-project topthink/think=版本号 项目名称

Ok, então registre o pit de hoje novamente.
Porque troquei o computador, fiz o upload do código para o gitee, depois baixei o código e não deu para rodar, foi estranho.
O principal é o pacote Firebase\JWT\JWT . A codificação e decodificação do JWT são diferentes das anteriores.
Primeiro compare o código antes e depois

código anterior

 private static function generateToken($data){
    
    
     $key = 'key';   //秘钥:自定义
     $payload = array(
         'iss' => 'my',                      //签发人(官方字段:非必需)
         'aud' => 'public',                    //受众(官方字段:非必需)
         'iat' => time(),                      //签发时间
         'nbf' => time(),                      //生效时间,立即生效
         'exp' => time() + 60*60*24*7,         //过期时间,一周
         'data' => $data,                        //自定义字段
     );
     //加密生成token
     return JWT::encode($payload, $key);
 }

    public static function checkToken($request){
    
    
        $authorization = $request->header("authorization");
        // 获取token
        {
    
    
            // 异常捕获无效
            try {
    
    
                $token = substr($authorization,8,-1);
            }catch (\Exception $ex){
    
    
                $token = $authorization;
            }
        }

        try {
    
    
            // 1.如果当前时间大于 exp,或者小于nbf,token无效,进行拦截
            $key = 'key';
            JWT::$leeway = 60;//当前时间减去60,把时间留点余地
            $decode = JWT::decode($token, $key, array('HS256'));
            // 查数据库,用户不存在
            if(Users::where('uid', $decode->data)->find()){
    
    
                // 比较当前时间大于 exp,或者小于nbf,token无效,进行拦截
                if($decode->nbf > time()){
    
    
                    return "权限伪造!";
                }elseif ($decode->exp < time()){
    
    
                    return "权限过期,请重新登录!";
                }
            }else{
    
    
                return "账户不存在!";
            }
        }catch (\Exception $ex){
    
    
            // token 无效
            return "权限不足!";
        }
        return true;
    }

código depois

private static function generateToken($data): string
    {
    
    
        $key = 'key';   //秘钥:自定义
        $payload = array(
            'iss' => 'my',                      //签发人(官方字段:非必需)
            'aud' => 'public',                    //受众(官方字段:非必需)
            'iat' => time(),                      //签发时间
            'nbf' => time(),                      //生效时间,立即生效
            'exp' => time() + 60*60*24*7,         //过期时间,一周
            'data' => $data,                        //自定义字段
        );

        $keyId = "keyId";
        //加密生成token
        return JWT::encode($payload, $key, 'HS256', $keyId);
    }

    public static function checkToken($request){
    
    
        $authorization = $request->header("authorization");
        // 获取token
        {
    
    
            // 异常捕获无效
            try {
    
    
                $token = substr($authorization,7);
            }catch (\Exception $ex){
    
    
                $token = $authorization;
            }
        }
        try {
    
    
            // 1.如果当前时间大于 exp,或者小于nbf,token无效,进行拦截
            JWT::$leeway = 60;//当前时间减去60,把时间留点余地
            // ==========================主要是下面这一段================================
            $key = new Key('key', 'HS256');
            $decode = JWT::decode($token, $key);
            // ==========================主要是上面面这一段================================
            // 查数据库,用户不存在
            if(Users::where('uid', $decode->data)->find()){
    
    
                // 比较当前时间大于 exp,或者小于nbf,token无效,进行拦截
                if($decode->nbf > time()){
    
    
                    return "权限伪造!";
                }elseif ($decode->exp < time()){
    
    
                    return "权限过期,请重新登录!";
                }
            }else{
    
    
                return "账户不存在!";
            }
        }catch (\Exception $ex){
    
    
            // token 无效
            echo $ex;
            return "权限不足!";
        }
        return true;
    }

Retire as partes principais das duas seções de código antes e depois e compare-as:

# encode
$key = 'key';   //秘钥:自定义
$payload = array(...);
return JWT::encode($payload, $key);
# decode
$key = 'key';
JWT::$leeway = 60;//当前时间减去60,把时间留点余地
$decode = JWT::decode($token, $key, array('HS256'));
===================================================================
# encode
$key = 'key';   //秘钥:自定义
$payload = array(...);
$keyId = "keyId"; //这个东西必须要加上,不加上,报错,报错内容:'"kid" empty, unable to lookup correct key'
//加密生成token
return JWT::encode($payload, $key, 'HS256', $keyId);
# decode
JWT::$leeway = 60;//当前时间减去60,把时间留点余地
$key = new Key('key', 'HS256'); // 必须是 Firebase\JWT\Key;的对象
$decode = JWT::decode($token, $key);

Analisando o código fonte
é principalmente $keyId aqui. Este parâmetro dá um valor inicial nulo, e este valor $header['kid'] = $keyId;, mas ao decodificar, deve ter um valor. Como a decodificação é necessária, há deve ser O valor não é nulo, então o valor inicial não será dado aqui, basta passar o parâmetro para o desenvolvedor, que é mais amigável.

public static function encode(
        array $payload,
        $key,
        string $alg,
        string $keyId = null,
        array $head = null
    ): string {
    
    
        $header = ['typ' => 'JWT', 'alg' => $alg];
        if ($keyId !== null) {
    
    
            $header['kid'] = $keyId; // 这里给$header['kid']赋值了,kid出错,原因在这
        }
        if (isset($head) && \is_array($head)) {
    
    
            $header = \array_merge($head, $header);
        }
        $segments = [];
        $segments[] = static::urlsafeB64Encode((string) static::jsonEncode($header));
        $segments[] = static::urlsafeB64Encode((string) static::jsonEncode($payload));
        $signing_input = \implode('.', $segments);
        $signature = static::sign($signing_input, $key, $alg);
        $segments[] = static::urlsafeB64Encode($signature);
        return \implode('.', $segments);
    }
public static function decode(
        string $jwt,
        $keyOrKeyArray
    ): stdClass {
    
    
        // Validate JWT
        $timestamp = \is_null(static::$timestamp) ? \time() : static::$timestamp;

        if (empty($keyOrKeyArray)) {
    
    
            throw new InvalidArgumentException('Key may not be empty');
        }
        $tks = \explode('.', $jwt);
        if (\count($tks) != 3) {
    
    
            throw new UnexpectedValueException('Wrong number of segments');
        }
        // 对token进行了切分,分成了三部分
        list($headb64, $bodyb64, $cryptob64) = $tks;
        $headerRaw = static::urlsafeB64Decode($headb64);
        if (null === ($header = static::jsonDecode($headerRaw))) {
    
    
            throw new UnexpectedValueException('Invalid header encoding');
        }
        $payloadRaw = static::urlsafeB64Decode($bodyb64);
        if (null === ($payload = static::jsonDecode($payloadRaw))) {
    
    
            throw new UnexpectedValueException('Invalid claims encoding');
        }
        if (\is_array($payload)) {
    
    
            // prevent PHP Fatal Error in edge-cases when payload is empty array
            $payload = (object) $payload;
        }
        if (!$payload instanceof stdClass) {
    
    
            throw new UnexpectedValueException('Payload must be a JSON object');
        }
        $sig = static::urlsafeB64Decode($cryptob64);
        if (empty($header->alg)) {
    
    
            throw new UnexpectedValueException('Empty algorithm');
        }
        if (empty(static::$supported_algs[$header->alg])) {
    
    
            throw new UnexpectedValueException('Algorithm not supported');
        }
        // 主要是这里,要求$header->kid 不为null,值为null就报错。
        $key = self::getKey($keyOrKeyArray, property_exists($header, 'kid') ? $header->kid : null);

		// 这里对$key 要求是 Firebase\JWT\Key ,不然 没有getAlgorithm()()方法就拿不到Algorithm值
        // Check the algorithm
        if (!self::constantTimeEquals($key->getAlgorithm(), $header->alg)) {
    
    
            // See issue #351
            throw new UnexpectedValueException('Incorrect key for this algorithm');
        }
        if ($header->alg === 'ES256' || $header->alg === 'ES384') {
    
    
            // OpenSSL expects an ASN.1 DER sequence for ES256/ES384 signatures
            $sig = self::signatureToDER($sig);
        }
        // 这里对$key 要求是 Firebase\JWT\Key ,不然 没有getKeyMaterial()方法就拿不到Material值
        if (!self::verify("$headb64.$bodyb64", $sig, $key->getKeyMaterial(), $header->alg)) {
    
    
            throw new SignatureInvalidException('Signature verification failed');
        }

        // Check the nbf if it is defined. This is the time that the
        // token can actually be used. If it's not yet that time, abort.
        if (isset($payload->nbf) && $payload->nbf > ($timestamp + static::$leeway)) {
    
    
            throw new BeforeValidException(
                'Cannot handle token prior to ' . \date(DateTime::ISO8601, $payload->nbf)
            );
        }

        // Check that this token has been created before 'now'. This prevents
        // using tokens that have been created for later use (and haven't
        // correctly used the nbf claim).
        if (isset($payload->iat) && $payload->iat > ($timestamp + static::$leeway)) {
    
    
            throw new BeforeValidException(
                'Cannot handle token prior to ' . \date(DateTime::ISO8601, $payload->iat)
            );
        }

        // Check if this token has expired.
        if (isset($payload->exp) && ($timestamp - static::$leeway) >= $payload->exp) {
    
    
            throw new ExpiredException('Expired token');
        }
        return $payload;
    }

おすすめ

転載: blog.csdn.net/wjl__ai__/article/details/124890239