JWT

  • 参考文档

http://121.5.151.41/mylesson/Django/%E7%BE%8E%E5%A4%9A%E5%95%86%E5%9F%8E%E9%A1%B9%E7%9B%AE%E8%AF%BE%E4%BB%B6/C02-Users/JWT.html

基于token的鉴权机制

- 基于token的鉴权机制类似于http协议也是无状态的
- 它不需要在服务端去保留用户的认证信息或者会话信息
- 这就意味着基于token认证机制的应用不需要去考虑用户在哪一台服务器登录了
- 这就为应用的扩展提供了便利
  • 流程
- 用户使用用户名密码来请求服务器
- 服务器进行验证用户的信息
- 服务器通过验证发送给用户一个token
- 客户端存储token,并在每次请求时附送上这个token值
- 服务端验证token值,并返回数据

- 注意事项
    - 这个token必须要在每次请求时传递给服务端,它应该保存在请求头里
    - 服务端要支持CORS(跨来源资源共享)策略,一般我们在服务端这么做就可以了 Access-Control-Allow-Origin: *

JWT长什么样?

- JWT是由三段信息构成的,将这三段信息文本用.链接一起就构成了Jwt字符串
- eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiIxMjM0NTY3ODkwIiwibmFtZSI6IkpvaG4gRG9lIiwiYWRtaW4iOnRydWV9.TJVA95OrM7E2cBab30RMHrHDcEfxjoYZgeFONFh7HgQ

JWT的构成

- 第一部分我们称它为头部(header),
- 第二部分我们称其为载荷(payload, 类似于飞机上承载的物品)
- 第三部分是签证(signature).
  • header: jwt的头部承载两部分信息

    • 声明类型,这里是jwt
    • 声明加密的算法 通常直接使用 HMAC SHA256
    • 例如下面的json
{
  'typ': 'JWT',
  'alg': 'HS256'
}

然后将头部进行base64加密(该加密是可以对称解密的),构成了第一部分

eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9

payload(载荷),是存放有效信息的地方(包含三部分),这个名字像是特指飞机上承载的货品

- 标准中注册的声明
- 公共的声明
- 私有的声明
  • 标准中注册的声明 (建议但不强制使用)
- iss: jwt签发者
- sub: jwt所面向的用户
- aud: 接收jwt的一方
- exp: jwt的过期时间,这个过期时间必须要大于签发时间
- nbf: 定义在什么时间之前,该jwt都是不可用的.
- iat: jwt的签发时间
- jti: jwt的唯一身份标识,主要用来作为一次性token,从而回避重放攻击
  • 公共声明: 公共的声明可以添加任何的信息
- 一般添加用户的相关信息或其他业务需要的必要信息
- 但不建议添加敏感信息,因为该部分在客户端可解密
  • 私有的声明 : 私有声明是提供者和消费者所共同定义的声明
一般不建议存放敏感信息,因为base64是对称解密的,意味着该部分信息可以归类为明文信息
  • 定义一个payload,然后将其进行base64加密,得到JWT的第二部分
{
  "sub": "1234567890",
  "name": "John Doe",
  "admin": true
}
......
eyJzdWIiOiIxMjM0NTY3ODkwIiwibmFtZSI6IkpvaG4gRG9lIiwiYWRtaW4iOnRydWV9

signature 是一个签证信息,这个签证信息由三部分组成

- header (base64后的)
- payload (base64后的)
- secret(最关键的部分)

- 这个部分需要base64加密后的header和base64加密后的payload使用.连接组成的字符串
然后通过header中声明的加密方式进行加盐secret组合加密,然后就构成了jwt的第三部分

# js 代码演示
var encodedString = base64UrlEncode(header) + '.' + base64UrlEncode(payload);
var signature = HMACSHA256(encodedString, 'secret'); # TJVA95OrM7E2cBab30RMHrHDcEfxjoYZgeFONFh7HgQ
  • 将 header, payload, signature 用"."连接成一个完整字符串,构成了最终的jwt
eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiIxMjM0NTY3ODkwIiwibmFtZSI6IkpvaG4gRG9lIiwiYWRtaW4iOnRydWV9.TJVA95OrM7E2cBab30RMHrHDcEfxjoYZgeFONFh7HgQ

- 注意事项

注意:secret是保存在服务器端的,jwt的签发生成也是在服务器端的
secret就是用来进行jwt的签发和jwt的验证
所以,它就是你服务端的私钥,在任何场景都不应该流露出去
一旦客户端得知这个secret, 那就意味着客户端是可以自我签发jwt了。

如何应用: 一般是在请求头里加入Authorization,并加上Bearer标注

fetch('api/user/1', {
  headers: {
    'Authorization': 'Bearer ' + token
  }
})

小结

  • 优点
- 因为json的通用性,所以JWT是可以进行跨语言支持的,像JAVA,JavaScript,NodeJS,PHP等很多语言都可以使用
- 因为有了payload部分,所以JWT可以在自身存储一些其他业务逻辑所必要的非敏感信息
- 便于传输,jwt的构成非常简单,字节占用很小,所以它是非常便于传输的
- 它不需要在服务端保存会话信息, 所以它易于应用的扩展
  • 安全相关
- 不应该在jwt的payload部分存放敏感信息,因为该部分是客户端可解密的部分
- 保护好secret私钥,该私钥非常重要
- 如果可以,请使用https协议

加密与解密

  • 使用工具网址
- BASE64编码解码工具:http://tools.jb51.net/transcoding/base64

- 在线图片转换BASE64工具:http://tools.jb51.net/transcoding/img2base64

- Base64在线编码解码 UTF-8版:http://tools.jb51.net/tools/base64_decode-utf8.php

- Base64在线编码解码 gb2312版:http://tools.jb51.net/tools/base64_decode-gb2312.php

python base64库简单使用

  • str类型不能直接加密(没有去找base64库是否直接支持),必须先转换为bytes类型,再加密/解密
import base64

my_string = 'king'
data = my_string.encode('utf-8') # 先转换成bytes

secret_string = base64.b64encode(data) # 加密
print(secret_string) # b'a2luZw=='

b_string = base64.b64decode(secret_string) # 解密
print(b_string) # b'king'

new_data = b_string.decode('utf-8') # 还原回来
print(new_data) # king
  • 加密图片并还原demo演示
read_img = open('dondon.png','rb')
read_data = read_img.read() # 获取图片的二进制byte数据
# print(read_data) # b'\xff\xd8\xff\xe0\x00\x10JFIF\x00\x01\x01\x00\x0......
read_img.close()

secret_data = base64.b64encode(read_data) # 加密(平时可以保存的密文)
# print(secret_data) # b'/9j/4AAQSkZJRgABAQAAAQABAAD/2wBDABQODxIPDRQSEBIXFRQYHjIhH......

b_data = base64.b64decode(secret_data) # 解密
print(b_data) # b'\xff\xd8\xff\xe0\x00......

write_img = open('new_dondon.png','wb')
write_img.write(b_data) # 把图片还原回来,保存
write_img.close()