JWT库

JWT的定义及其组成

转自 https://www.jianshu.com/p/168d34aab2e3

JWT(JSON Web Token)是一个非常轻巧的规范。这个规范允许我们使用JWT在用户和服务器之间传递安全可靠的信息。

一个JWT实际上就是一个字符串,它由三部分组成,头部、载荷与签名。

载荷(Payload)

我们先将用户认证的操作描述成一个JSON对象。其中添加了一些其他的信息,帮助今后收到这个JWT的服务器理解这个JWT。

{
    "sub": "1",
    "iss": "http://localhost:8000/auth/login",
    "iat": 1451888119,
    "exp": 1454516119,
    "nbf": 1451888119,
    "jti": "37c107e4609ddbcc9c096ea5ee76c667"
}

这里面的前6个字段都是由JWT的标准所定义的。

sub: 该JWT所面向的用户
iss: 该JWT的签发者
iat(issued at): 在什么时候签发的token
exp(expires): token什么时候过期
nbf(not before):token在此时间之前不能被接收处理
jti:JWT ID为web token提供唯一标识

这些定义都可以在标准中找到。

将上面的JSON对象进行base64编码可以得到下面的字符串:

eyJzdWIiOiIxIiwiaXNzIjoiaHR0cDpcL1wvbG9jYWx
ob3N0OjgwMDFcL2F1dGhcL2xvZ2luIiwiaWF0IjoxNDUxODg4MTE5LCJleHAiOjE0NTQ1MTYxMTksIm5iZiI6MTQ1MTg4OD
ExOSwianRpIjoiMzdjMTA3ZTQ2MDlkZGJjYzljMDk2ZWE1ZWU3NmM2NjcifQ

这个字符串我们将它称作JWT的Payload(载荷)。

如果你使用Node.js,可以用Node.js的包base64url来得到这个字符串:


var base64url = require('base64url')
var header = {
    "from_user": "B",
    "target_user": "A"
}

console.log(base64url(JSON.stringify(header)))

注:Base64是一种编码,也就是说,它是可以被翻译回原来的样子来的。它并不是一种加密过程。
头部(Header)

JWT还需要一个头部,头部用于描述关于该JWT的最基本的信息,例如其类型以及签名所用的算法等。这也可以被表示成一个JSON对象:

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

在这里,我们说明了这是一个JWT,并且我们所用的签名算法(后面会提到)是HS256算法。

对它也要进行Base64编码,之后的字符串就成了JWT的Header(头部):

eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9

签名(签名)

将上面的两个编码后的字符串都用句号.连接在一起(头部在前),就形成了:

eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJzdWIiOiIxIiwiaXNzIjoiaHR0cDpcL1wvbG9jYWx
ob3N0OjgwMDFcL2F1dGhcL2xvZ2luIiwiaWF0IjoxNDUxODg4MTE5LCJleHAiOjE0NTQ1MTYxMTksIm5iZiI6MTQ1MTg4OD
ExOSwianRpIjoiMzdjMTA3ZTQ2MDlkZGJjYzljMDk2ZWE1ZWU3NmM2NjcifQ

最后,我们将上面拼接完的字符串用HS256算法进行加密。在加密的时候,我们还需要提供一个密钥(secret):

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

这样就可以得到我们加密后的内容:

wyoQ95RjAyQ2FF3aj8EvCSaUmeP0KUqcCJDENNfnaT4

这一部分又叫做签名。

最后将这一部分签名也拼接在被签名的字符串后面,我们就得到了完整的JWT:


eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJzdWIiOiIxIiwiaXNzIjoiaHR0cDpcL1wvbG9jYWx
ob3N0OjgwMDFcL2F1dGhcL2xvZ2luIiwiaWF0IjoxNDUxODg4MTE5LCJleHAiOjE0NTQ1MTYxMTksIm5iZiI6MTQ1MTg4OD
ExOSwianRpIjoiMzdjMTA3ZTQ2MDlkZGJjYzljMDk2ZWE1ZWU3NmM2NjcifQ.wyoQ95RjAyQ2FF3aj8EvCSaUmeP0KUqcCJDENNfnaT4

JWT C/C++ 语言库

https://jwt.io/ 在这个网页中列举了各种主流语言中JWT的实现。

https://github.com/benmcollins/libjwt 这个C语言库的star较多,时间较长,所以应该比较稳定。根据README中所说,可以在Ubuntu中直接安装。下段要介绍的就是这个库。

https://github.com/arun11299/cpp-jwt 这个库比较新,A C++14 library for JWT。Tested with clang-5.0 and g++-6.4.  VS2017 is also supported.
因为项目不支持C++14,所以没有使用这个库,但是,看起来很有前途的样子。

JWT C Library

 一、编译(VC++)

  1、下载源码。
  2、下载并编译依赖库:https://github.com/akheron/jansson
    json库不依赖于其他库,使用cmake直接生成VS工程,然后编译就OK。
  3、下载并编译openssl库。
  4、因为对cmake不熟,所以直接将libjwt中的文件添加到新建的VS工程中,在VS中配置好json库和openssl库。
  5、jansson,openssl都可以编译为静态库,所以libjwt也可以编译为静态库。
  6、头文件只有一个jwt.h

二、接口说明

  1、接口说明在头文件中
  2、例子:

#include "jwt.h"

int main()
{
    const char *json = "{\"id\":\"FVvGYTr3FhiURCFebsBOpBqTbzHdX/DvImiA2yheXr8=\","
        "\"iss\":\"localhost\"}";

    // jwt 指针
    jwt_t *jwt = NULL;

    // 密钥
    const char key[] = "My Passphrase";

    // 生成jwt对象
    int ret = jwt_new(&jwt);
    assert(ret == 0);
    assert(jwt != NULL);
    printf("空的jwt对象\n%s\n", jwt_dump_str(jwt, 1));

    // 添加字符串内容
    ret = jwt_add_grant(jwt, "iss", "files.cyphre.com");
    assert(ret == 0);
    printf("添加字符串内容\n%s\n", jwt_dump_str(jwt, 1));

    // 添加整数内容
    ret = jwt_add_grant_int(jwt, "iat", (long)time(NULL));
    assert(ret == 0);
    printf("添加整数内容\n%s\n", jwt_dump_str(jwt, 1));

    // 添加json内容
    // 上述iat,iss会被添加到json中
    // 上述iss会被json中的iss覆盖
    ret = jwt_add_grants_json(jwt, json);
    assert(ret == 0);
    printf("添加json内容\n%s\n", jwt_dump_str(jwt, 1));

    // 设置算法和密钥
    // 会自动生成header部分
    ret = jwt_set_alg(jwt, JWT_ALG_HS256, (unsigned char *)key,    strlen(key));
    assert(ret == 0);
    printf("设置算法和密钥\n%s\n", jwt_dump_str(jwt, 1));
    
    // 输出最终的jwt token
    char *jwt_str = jwt_encode_str(jwt);
    printf("输出最终的jwt\n%s\n", jwt_str);

    // 用来存放decode后的jwt对象
    jwt_t *jwt2 = NULL;

    // decode
    ret = jwt_decode(&jwt2, jwt_str, (const char unsigned *)key, strlen(key));
    assert(ret == 0);

    // decode后的json内容
    printf("\nDecode后的json内容\n%s\n", jwt_dump_str(jwt2, 1));

    jwt_free(jwt);
    jwt_free(jwt2);

    return 0;
}

  输出如下:

空的jwt对象
{
    "alg": "none"
}
.
{}

添加字符串内容
{
    "alg": "none"
}
.
{
    "iss": "files.cyphre.com"
}

添加整数内容
{
    "alg": "none"
}
.
{
    "iat": 1526283284,
    "iss": "files.cyphre.com"
}

添加json内容
{
    "alg": "none"
}
.
{
    "iat": 1526283284,
    "id": "FVvGYTr3FhiURCFebsBOpBqTbzHdX/DvImiA2yheXr8=",
    "iss": "localhost"
}

设置算法和密钥
{
    "typ": "JWT",
    "alg": "HS256"
}
.
{
    "iat": 1526283284,
    "id": "FVvGYTr3FhiURCFebsBOpBqTbzHdX/DvImiA2yheXr8=",
    "iss": "localhost"
}

输出最终的jwt
eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJpYXQiOjE1MjYyODMyODQsImlkIjoiRlZ2R1lUcjN
GaGlVUkNGZWJzQk9wQnFUYnpIZFgvRHZJbWlBMnloZVhyOD0iLCJpc3MiOiJsb2NhbGhvc3QifQ.aeBf
BIgoqWA8Kcz6wwrHE-3HIvSfix8K3cZaS98O5zU

Decode后的json内容
{
    "typ": "JWT",
    "alg": "HS256"
}
.
{
    "iat": 1526283284,
    "id": "FVvGYTr3FhiURCFebsBOpBqTbzHdX/DvImiA2yheXr8=",
    "iss": "localhost"
}

 

posted @ 2018-05-14 11:30  Droplet  阅读(1878)  评论(0编辑  收藏  举报