PHP实现JWT的token登录

2019-08-05  本文已影响0人  Ben大师

下面是完整的类的代码,使用也非常的简单,一个是getToken(),另一个是verifyToken()

class Jwt
{
    //这个是头部
    private static $header=array(
        'alg' => 'HS256',//生成signature的算法
        'typ' => 'JWT'//类型
    );

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

    /**
     * 获取jwt token
     * @param array $payload jwt荷载
     * @return bool|string
     */
    public static function getToken(array $payload){
        if(is_array($payload)){
            $base64header = self::base64UrlEncode(json_encode(self::$header,JSON_UNESCAPED_UNICODE));
            $base64payload = self::base64UrlEncode(json_encode($payload, JSON_UNESCAPED_UNICODE));
            $base64signature = self::signature($base64header . '.' . $base64payload, self::$key, self::$header['alg']);
            return $token = $base64header . '.' . $base64payload . '.' . $base64signature;
        }else{
            return false;
        }
    }

    /**
     * 验证token是否有效,默认值验证exp时间
     * @param string $token 需要验证的token
     * @return bool|mixed 如果正确,那么,将返回数组$payload
     */
    public static function verifyToken(string $token){
        $tokens = explode('.', $token);
        if (count($tokens) != 3){
            return false;
        }
        list($base64header, $base64payload, $sign) = $tokens;

        //获取header部分
        $base64decodeheader = json_decode(self::base64UrlDecode($base64header),true);
        if (empty($base64decodeheader['alg'])){
            return false;
        }

        //签名验证,就是将现成的$base64header和$base64payload重新加密一遍,看是不是等于$sing
        $calc_sign = self::signature(
            $base64header . '.' . $base64payload,
            self::$key,
            $base64decodeheader['alg'],
            );
        if ($calc_sign != $sign){
            return false;
        }

        //获得payload
        $payload = json_decode(self::base64UrlDecode($base64payload),true);

        //核对过期时间是不是小于现在的时间,如果是,那就返回false
        if (isset($payload['exp']) && $payload['exp'] < time()){
            return false;
        }

        return $payload;
    }

    /**
     * 首先用base64_encode编码,然后换掉+/=这3个符号
     * 为什么不直接用urlEncode?
     * 我觉得可能是为了减少运算吧,反正就这3个符号,没有更多。
     * @param string $input 需要编码的字符串
     * @return string 编码后的字符串
     */
    private static function base64UrlEncode(string $input){
        return str_replace('=','',strtr(base64_encode($input),'+/','-_'));
    }

    /**
     * 解码由base64UrlEncode()编码的字符串
     * @param string $input
     * @return bool|string 解码后的字符串
     */
    private static function base64UrlDecode(string $input){
        $remainder = strlen($input) % 4;
        if ($remainder) {
            $addlen = 4 - $remainder;
            $input .= str_repeat('=', $addlen); //差了几个=就补上几个等号
        }
        return base64_decode(strtr($input, '-_', '+/'));
    }

    /**
     * HMACSHA256签名
     * @param string $input 为base64UrlEncode(header).".".base64UrlEncode(payload)
     * @param string $key 秘钥
     * @param string $alg 算法
     * @return string 返回签名的编码后的字符串
     */
    private static function signature(string $input, string $key, string $alg = 'HS256'){
        $alg_config = array(
            'HS256' => 'sha256'
        );
        $signature = hash_hmac($alg_config[$alg], $input, $key, true);
        return self::base64UrlEncode($signature);
    }
}

贴上我在ThinkPHP中的测试代码:

public function testToken(){
    $payload = [
        'exp' => time() + 9000,
        'name' => 'user',
        'jti' => '11111233'
    ];
    return $token = Jwt::getToken($payload);
}

public function testToken2(){
    $token = 'eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJleHAiOjE1NjUwMTk2NzIsIm5hbWUiOiLmsZ_niofniociLCJqdGkiOiIxMTExMTIzMyJ9.mgSMwnpHQiIlBYNGTphAsYWc2pTiygj1AhmQU_p1OBw';
    $payload = Jwt::verifyToken($token);
    dump($payload);
}

上面的代码是基于这个代码修改的(其实没有改多少,只是改了一些说明):https://www.jb51.net/article/146790.htm

上一篇下一篇

猜你喜欢

热点阅读