jwt原理与应用-php

官网:https://jwt.io/
官网有案例测试。

jwt-framework
github:https://github.com/web-token/jwt-framework
文档:https://web-token.spomky-labs.com/
composer require web-token/jwt-framework

为什么要使用JWT
JWT是json web token缩写。它将用户信息加密到token里,服务器不保存任何用户信息。服务器通过使用保存的密钥验证token的正确性,只要正确即通过验证。基于token的身份验证可以替代传统的cookie+session身份验证方法。

它定义了一种用于简洁,自包含的用于通信双方之间以 JSON 对象的形式安全传递信息的方法。JWT 可以使用 HMAC 算法或者是 RSA 的公钥密钥对进行签名。它具备两个特点:

简洁(Compact):可以通过URL, POST 参数或者在 HTTP header 发送,因为数据量小,传输速度快

自包含(Self-contained):负载中包含了所有用户所需要的信息,避免了多次查询数据库

JWT由三个部分组成:header.payload.signature

1、header部分:

{
  "typ": "JWT",
  "alg": "HS256"
}

对应base64UrlEncode编码为:eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9
说明:该字段为json格式。alg字段指定了生成signature的算法,默认值为HS256,typ默认值为JWT

2、payload部分:

{
  "iss": "xxxx",//该JWT的签发者
  "iat": 1569746556,//签发时间
  "exp": 1601282556,//过期时间
  "nbf": 1569746556,//该时间之前不接收处理该Token
  "jti": "NSiLlGKANgTRA9WD",//该Token唯一标识
  "sub": xxxxxxx,//面向的用户,比如用户id
  "prv": "f6b71549db8c2c42b75827aa44f02b7ee529d24d"
}

对应base64UrlEncode编码为:eyJpc3MiOiJodHRwOi8vMTcyLjMxLjIwNS43MC91Yy91Y2VudGVyL3JlQmluZGluZyIsImlhdCI6MTU2OTc0NjU1NiwiZXhwIjoxNjAxMjgyNTU2LCJuYmYiOjE1Njk3NDY1NTYsImp0aSI6Ik5TaUxsR0tBTmdUUkE5V0QiLCJzdWIiOjkzMjYwOSwicHJ2IjoiZjZiNzE1NDlkYjhjMmM0MmI3NTgyN2FhNDRmMDJiN2VlNTI5ZDI0ZCJ9
说明:该字段为json格式,表明用户身份的数据,可以自己自定义字段,很灵活。

3、signature部分:

HMACSHA256(
 base64UrlEncode(header) + "." +
 base64UrlEncode(payload),
 123456
)

说明:对header和payload进行base64UrlEncode编码后进行拼接。通过key(这里是123456)进行HS256算法签名。
对应的签名为:wwTa9XRLydYmrjCOgiB4Wq1_rzGZ4sVeLg3NZVr0SaA

4、最终得到的结果就是三段的拼接:header.payload.signature,即
eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJpc3MiOiJodHRwOi8vMTcyLjMxLjIwNS43MC91Yy91Y2VudGVyL3JlQmluZGluZyIsImlhdCI6MTU2OTc0NjU1NiwiZXhwIjoxNjAxMjgyNTU2LCJuYmYiOjE1Njk3NDY1NTYsImp0aSI6Ik5TaUxsR0tBTmdUUkE5V0QiLCJzdWIiOjkzMjYwOSwicHJ2IjoiZjZiNzE1NDlkYjhjMmM0MmI3NTgyN2FhNDRmMDJiN2VlNTI5ZDI0ZCJ9.wwTa9XRLydYmrjCOgiB4Wq1_rzGZ4sVeLg3NZVr0SaA

5、使用
一般将生成的jwt串放在请求头的 Authorization 中传递,服务端来验证。

在使用 JWT 时需要注意以下事项:

  1. JWT 默认不加密,如果要写入敏感信息必须加密,可以用生成的原始令牌再次对内容进行加密;
  2. JWT 无法使服务器保存会话状态,当令牌生成后在有效期内无法取消也不能更改;
  3. JWT 包含认证信息,如果泄露了,任何人都可以获得令牌所有的权限;因此 JWT 有效期不能太长,对于重要操作每次请求都必须进行身份验证。

代码:

 'HS256', //生成signature的算法
        'typ' => 'JWT'  //类型
    ];

    // payload
    private $payload;

    // 过期时间
    private $ttl = 10;// 10s
    private $refreshTtl = 7200;

    //使用HMAC生成信息摘要时所使用的密钥
    private $key = '123456';

    // 是否开启黑名单,开启后在校验token的时候会检查黑名单,影响性能,但是安全。
    private $openBlackList = false;

    public function __construct()
    {

    }

    // 设置载荷
    private function setPayload($pay = [])
    {
        $this->payload = [
            "iss" => "jwt_admin",//该JWT的签发者
            "iat" => time(),//签发时间
            "exp" => time() + $this->ttl,//过期时间
            "nbf" => time(),//该时间之前不接收处理该Token
            "jti" => md5(uniqid('JWT') . time()),//该Token唯一标识
            "sub" => '',//面向的用户,比如用户id
        ];
        $this->payload = array_merge($this->payload, $pay);
    }

    // 设置过期时间
    public function setTtl($seconds)
    {
        $seconds = intval($seconds);
        if ($seconds > 0) {
            $this->ttl = $seconds;
        }
    }

    // 生成token
    public function getToken($payload)
    {
        $this->setPayload($payload);

        if (is_array($this->payload)) {
            $base64header  = $this->base64UrlEncode(json_encode($this->header, JSON_UNESCAPED_UNICODE));
            $base64payload = $this->base64UrlEncode(json_encode($this->payload, JSON_UNESCAPED_UNICODE));
            $signature     = $this->signature($base64header . '.' . $base64payload, $this->key, $this->header['alg']);
            $token         = $base64header . '.' . $base64payload . '.' . $signature;
            return $token;
        } else {
            return false;
        }
    }

    // 验证token
    public function verifyToken($Token)
    {
        $tokens = explode('.', $Token);
        if (count($tokens) != 3)
            return false;

        list($base64header, $base64payload, $sign) = $tokens;

        //获取jwt算法
        $base64decodeheader = json_decode($this->base64UrlDecode($base64header), JSON_OBJECT_AS_ARRAY);
        if (empty($base64decodeheader['alg']))
            return false;

        //签名验证
        if ($this->signature($base64header . '.' . $base64payload, $this->key, $base64decodeheader['alg']) !== $sign)
            return false;

        $payload = json_decode($this->base64UrlDecode($base64payload), JSON_OBJECT_AS_ARRAY);

        // 判断黑名单 $payload['jti']
        if($this->openBlackList){

        }

        //签发时间大于当前服务器时间验证失败
        if (isset($payload['iat']) && $payload['iat'] > time())
            return false;

        //过期时间小宇当前服务器时间验证失败
        if (isset($payload['exp']) && $payload['exp'] < time())
            return false;

        //该nbf时间之前不接收处理该Token
        if (isset($payload['nbf']) && $payload['nbf'] > time())
            return false;

        return $payload;
    }

    // 刷新token延长过期时间
    public function refreshToken($token){

    }

    /**
     * 当token泄露,或者注销登录,将token加入黑名单,使用redis存储,key的过期时间应大于token的过期时间。
     * 一般将payload中的jti作为key,因为jti是唯一标识,value可以设置空,因为并不需要。
     */
    public function addBlackList(){
        if($this->openBlackList){

        }
    }


    /**
     * base64UrlEncode  https://jwt.io/ 中base64UrlEncode编码实现
     * 将 +/ 替换成 -_
     * 将 = 删除
     * @param string $input 需要编码的字符串
     * @return string
     */
    private function base64UrlEncode($input)
    {
        return str_replace('=', '', strtr(base64_encode($input), '+/', '-_'));
    }

    /**
     * base64UrlEncode https://jwt.io/ 中base64UrlEncode解码实现
     * @param string $input 需要解码的字符串
     * @return bool|string
     */
    private function base64UrlDecode($input)
    {
        $remainder = strlen($input) % 4;
        if ($remainder) {
            $addlen = 4 - $remainder;
            $input  .= str_repeat('=', $addlen);
        }
        return base64_decode(strtr($input, '-_', '+/'));
    }

    /**
     * HMACSHA256签名  https://jwt.io/ 中HMACSHA256签名实现
     * @param string $input 为base64UrlEncode(header).".".base64UrlEncode(payload)
     * @param string $key
     * @param string $alg 算法方式
     * @return mixed
     */
    private function signature($input, $key, $alg = 'HS256')
    {
        $alg_config = array(
            'HS256' => 'sha256'
        );
        return $this->base64UrlEncode(hash_hmac($alg_config[$alg], $input, $key, true));
    }
}

// 测试加密
$payload = array('sub' => '1234567890', 'name' => 'John Doe');
$jwt     = new Jwt;
$token   = $jwt->getToken($payload);
echo $token.PHP_EOL;

//对token进行验证签名
$getPayload = $jwt->verifyToken($token);
var_dump($getPayload);
echo PHP_EOL;

你可能感兴趣的:(PHP)