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对象 %s ", jwt_dump_str(jwt, 1)); // 添加字符串内容 ret = jwt_add_grant(jwt, "iss", "files.cyphre.com"); assert(ret == 0); printf("添加字符串内容 %s ", jwt_dump_str(jwt, 1)); // 添加整数内容 ret = jwt_add_grant_int(jwt, "iat", (long)time(NULL)); assert(ret == 0); printf("添加整数内容 %s ", jwt_dump_str(jwt, 1)); // 添加json内容 // 上述iat,iss会被添加到json中 // 上述iss会被json中的iss覆盖 ret = jwt_add_grants_json(jwt, json); assert(ret == 0); printf("添加json内容 %s ", jwt_dump_str(jwt, 1)); // 设置算法和密钥 // 会自动生成header部分 ret = jwt_set_alg(jwt, JWT_ALG_HS256, (unsigned char *)key, strlen(key)); assert(ret == 0); printf("设置算法和密钥 %s ", jwt_dump_str(jwt, 1)); // 输出最终的jwt token char *jwt_str = jwt_encode_str(jwt); printf("输出最终的jwt %s ", 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(" Decode后的json内容 %s ", 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" }