深入浅出:JWT(JSON Web Token)鉴权

索引:

  • 第一部分 基本概念
  • 第二部分 使用流程
  • 第三部分 实现代码

第一部分 基本概念

JWT就是一个字符串,经过加密处理与校验处理的字符串,由三个部分组成。基于token的身份验证可以替代传统的cookie+session身份验证方法。三个部分分别如下:

 header.payload.signature

header部分:

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

这就是一个json串,两个字段都是必须的,alg字段指定了生成signature的算法,默认值为 HS256,可以自己指定其他的加密算法,如RSA.经过base64encode就可以得到 header.
payload部分:

$payload=[
            'iss' => $issuer, //签发者
            'iat' => $_SERVER['REQUEST_TIME'], //什么时候签发的
            'exp' => $_SERVER['REQUEST_TIME'] + 7200 //过期时间
            'uid'=>1111
        ];

signature部分
将 header和 payload使用header中指定的加密算法加密,当然加密过程还需要自定秘钥,自己选一个字符串就可以了。

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

php实现代码:

<?php
 
    public static function encode(array $payload, string $key, string $alg = 'SHA256')
    {
        $key = md5($key);
        $jwt = self::urlsafeB64Encode(json_encode(['typ' => 'JWT', 'alg' => $alg])) . '.' . self::urlsafeB64Encode(json_encode($payload));
        return $jwt . '.' . self::signature($jwt, $key, $alg);
    }
 
   public static function signature(string $input, string $key, string $alg)
    {
        return hash_hmac($alg, $input, $key);
    }

将以上三个部分连起来就是JWT了。

参数解释


Screen-Shot-2018-09-06-at-3.24.30-PM.png

第二部分 JWT使用流程

1.png

实现步骤:

  • 用户登录:输入用户名和密码
  • 服务器进行用户名和密码的校验,通过之后生成JWT返回给前端
  • 每次用户请求API的时候都带上JWT
  • 服务端收到JWT做校验

第三部分 实现代码

首先composer安装一个JWT库,这里使用的是firebase的库,

composer require firebase/php-jwt

服务端在生成token时,加入少量的用户信息,比如用户的id。服务端接收到token之后,可以解析出这些数据,从而将token和用户关联了起来。

使用很简单,传入秘钥,生成Token:

private function generateToken($key){
        vendor('JWT.JWT');
        $key = $user_key;
        $token = array(
            "iss" => "https://feifei50.com", // Issued At Claim
            "aud" => "https://feifei50.com", // Audience Claim
            "iat" => $_SERVER['REQUEST_TIME'], // Issued At Claim
            "nbf" => $_SERVER['REQUEST_TIME']-300, // Not Before Claim
            "exp" => $_SERVER["REQUEST_TIME"]+3600 // Expire Time Claim
        );
        $jwt = JWT::encode($token, $key);
        return $jwt;
    }

返回的$jwt大概是这个样子:

//eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJpc3MiOiJodHRwczpcL1wvZmVpZmVpNTAuY29tIiwiYXVkIjoiaHR0cHM6XC9cL2ZlaWZlaTUwLmNvbSIsImlhdCI6MTUzNjI2OTM4NSwibmJmIjoxNTM2MjYxMDY1LCJleHAiOjE1MzYyNzk1NjV9.W19LwKH2eQbYs6YHKkbk7bOQK2dBfmV8jcNGIN4OGlY

key就是用来混淆的,自己随便定义好了,encode 需要key,decode 也需要key,这里用md5加密一下,会加入一个令牌混淆加密

在前端拿到这个token之后就将这个token放在LocalStroage中,每发送AJAX请求的时候都带上这个token给服务端,服务端解码这个token来做校验,如果校验没有通过,那么强制其跳回登录页面。

firebase的这个JWT会在解码的时候做部分的校验,比如key是否正确,token是否expired之类的,具体参考源码,很容易理解。以后后端每次拿到这组Token就要从Token中进行解码:

解码Token

private function decodeToken($jwt,$user_key){
    JWT::$leeway = 300; // $leeway in seconds
    $decoded = JWT::decode($jwt, $user_key, array('HS256'));
    return $decoded;
}

这样解码之后就拿到了之前定义的payload的信息

本文作者熊冰,个人网站Bing的天涯路,转载请注明出处。

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
平台声明:文章内容(如有图片或视频亦包括在内)由作者上传并发布,文章内容仅代表作者本人观点,简书系信息发布平台,仅提供信息存储服务。
禁止转载,如需转载请通过简信或评论联系作者。

推荐阅读更多精彩内容