文章目录
前言
随着分布式 web 应用的普及,通过 session 管理用户登录状态成本越来越高,JWT 这个 JSON 安全传输方案,被越来越多人用来进行用户身份认证。
LoRa Server 项目中正是使用了 JWT 认证方案。
小能手最近在学习 LoRa Server 项目,应该是最有影响力的 LoRaWAN 服务器开源项目。它组件丰富,代码可读性强,是个很好的学习资料。更多学习笔记,可点此查看。
1 JWT 基础
JWT 的基础资料,可以查看 jwt.io,或者查看阮一峰 JSON Web Token 入门教程。
1.1 原理
JWT 的原理是,服务器认证以后,生成一个包含用户信息的 JSON 对象,发回给用户。
以后,用户与服务端通信的时候,都要发回这个 JSON 对象。服务器完全只靠这个对象认定用户身份。为了防止用户篡改数据,服务器在生成这个对象的时候,会加上签名(详见后文)。
服务器就不保存任何 session 数据了,也就是说,服务器变成无状态了,从而比较容易实现扩展。
1.2 数据结构
JWT 是一个很长的字符串,xxxxx.yyyyy.zzzzz,中间用点(.)分隔成三个部分,依次为:Header(头部)、Payload(负载)、Signature(签名)
Header
Header 描述 JWT 的元数据,包含两部分:"alg"表示签名算法,"typ"表示token的类型。
{
"alg": "HS256",
"typ": "JWT"
}
Payload
Payload 包含典型的声明,用来存放实际需要传递的数据。JWT 规定了7个官方字段供选用。
声明名称 | 说明 |
---|---|
iss (issuer) | 签发人 |
exp (expiration time) | 过期时间 |
sub (subject) | 主题 |
aud (audience) | 受众 |
nbf (Not Before) | 生效时间 |
iat (Issued At) | 签发时间 |
jti (JWT ID) | 编号 |
Signature
Signature 部分是对前两部分的签名,防止数据篡改。
首先,需要指定一个密钥(secret)。这个密钥只有服务器才知道,不能泄露给用户。然后,使用 Header 里面指定的签名算法(默认是 HMAC SHA256),按照下面的公式产生签名。
HMACSHA256(
base64UrlEncode(header) + "." +
base64UrlEncode(payload),
secret)
1.3 使用
客户端每次与服务器通信,都要带上这个 JWT。放在 HTTP 请求的头信息Authorization字段里面。
Authorization: Bearer
2 进一步的认识 jwt
https://jwt.io/ 中包含了一个 JWT 的调试器,可以编码、校验、解码 JWT。
默认的 payload + 默认的密钥 your-256-bit-secret
{
"sub": "1234567890",
"name": "John Doe",
"iat": 1516239022
}
HMACSHA256(
base64UrlEncode(header) + "." +
base64UrlEncode(payload),
your-256-bit-secret
)
这样得到的 JWT 是:
eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiIxMjM0NTY3ODkwIiwibmFtZSI6IkpvaG4gRG9lIiwiaWF0IjoxNTE2MjM5MDIyfQ.SflKxwRJSMeKKF2QT4fwpMeJf36POk6yJV_adQssw5c
修改密钥为空
这样得到的 JWT 是:
eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiIxMjM0NTY3ODkwIiwibmFtZSI6IkpvaG4gRG9lIiwiaWF0IjoxNTE2MjM5MDIyfQ.he0ErCNloe4J7Id0Ry2SEDg09lKkZkfsRiGsdX_vgEg
可以发现只有 Signature 部分发生了变化。
修改 Payload
{
"sub": "1234567890",
"name": "User",
"iat": 1516239022
}
这样得到的 JWT 是:
eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiIxMjM0NTY3ODkwIiwibmFtZSI6IlVzZXIiLCJpYXQiOjE1MTYyMzkwMjJ9.v3ZSJLUS07wLr-6gFimTSiZ9rNbFrs4XeSPkNKmfdK4
发现 Payload 和 Signature 都发生了变化。
3 LoRa Server 中的 JWT 使用
3.1 REST API 请求的头
LoRa Server 中,每次 REST 请求需要使用 Grpc-Metadata-Authorization 头部来设置 JWT token。
3.2 实例拆解
我抓了一个 REST API 请求的 Grpc-Metadata-Authorization 头部,使用 JWT Debugger 来解码下。
eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJpc3MiOiJsb3JhLWFwcC1zZXJ2ZXIiLCJhdWQiOiJsb3JhLWFwcC1zZXJ2ZXIiLCJuYmYiOjE1NzEwNDg1NjMsImV4cCI6MTU3MTEzNDk2Miwic3ViIjoidXNlciIsInVzZXJuYW1lIjoiYWRtaW4ifQ.NVPnDBnhxn2_yWmZNAHpS7fPJ3rGrJIxAYyyq-pPG3g
粘贴到 JWT Debugger,便可得到解码后的 JSON 串:
{
"iss": "lora-app-server",
"aud": "lora-app-server",
"nbf": 1571048563,
"exp": 1571134962,
"sub": "user",
"username": "admin"
}
捕获这个包的人,可以知道这个包的内容,但如果他不知道密钥的话,那就无法篡改数据。