jwt 全称 json web token,是一种基于JSON的、用于在网络上安全的表示双方之间的声明。目前,jwt广泛应用在系统的用户认证方面,特别是现在前后端分离项目。
由于http 协议是一种无状态的协议,无法对客户端的身份进行认证和唯一标识,所以需要采用其他的手段来实现。
传统的cookie-sessio机制也是为了解决客户端的身份验证而出现的,服务器同通过保存客户端的数据,并发送给客户端一个随机字符串作为session_id,这样每次请求时服务器可以根据每个客户端的session_id来找到保存在服务器的数据,从而实现客户端的身份认证,但是这种方式在现在的使用种存在问题,例如session是保存在服务器的内存中(保证效率)的,这样会耗费大量的服务器内存资源,并且在目前的分布式的服务中,session存在与单独一台服务器中,而这个session无法与其他主机共享,已经登录的客户端请求转到其他服务器上将会丢失客户端状态。
上述问题的存在总的来说还是session保存到服务端,并且没有进行共享而导致的,解决的方案是使用单独的session服务,保证session信息共享,这也是常用的做法,例如使用一个redis集群作为session储存的解决方案。
另一种解决方式则是使 jwt 的方案,它不同于将客户端的信息保存到服务器,而是通过一个jwt字符串将这些数据发送到对应的客户端,让每个客户端各自保存自己的数据,下一次请求服务器时,带上这个jwt字符串,从中获取这个字符串中获取数据即可,并且为了解决数据保存到客户端而存在被篡改的问题,jwt 使用一个算法将 数据+盐 生成一个签名添加到字符串中,这个签名只有知道盐的服务器了可以生成能被自己验证通过的签名。所以客户端想要篡改数据,就必须能够根据数据生成这个签名,也就需要服务器的这个盐。只要盐是安全的,他人就只能通过其他方式破解,这种概率是比较低的。
jwt是一个固定格式的,以点分成三段的字符串,形式如下。
aDJIGghOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiIxMjM0NTY3ODkwIiwibmFtZSI6IkpSDGfvaG4gRG9lSGEIiwiaDSGWF0IjoxNTE2MjM5MDIyfQ.SflKxwRJSMeKKF2QT4fwpMeJf36POk6yJV_adQssw5c
三个部分有各自的作用,前两部分都是可以通过BASE64反解得到明文数据的,而第三部分则是更具前两部分生成的签名。分别介绍以下三部分的内容以及生成过程。
第一段被称为HEADER部分,包含固定的算法和token类型数据,然后将该数据的json字符串进行base64url编码得到。该头信息说明了这是一个JWT类型的token,并且在生成签名时候,使用的是HS256算法。
{ "alg": "HS256", "typ": "JWT" }
第二部分被称为payload部分,这个部分可以包含用户的个人信息内容,用于保存用户的状态信息等,然后同样的通过base64url编码,例如可以记录用户的id,姓名等,但是不能保存敏感的信息,例如密码等,因为这部分数据可以被反解,所以等同于明文保存,如果该字符串被其他恶意用户获得,将会造成严重的安全问题。
{ "UserId": "1001", "Name": "tom", "iat": 1516239022 ... }
数据使用json的格式保存即可,示例中UserId和Name都是自定义的数据,而iat是JWT的保留字,该key代表了特殊的含义,即该字符串签发时的时间戳信息,同时还可以指定过期时间等信息,这些保留的关键字包括:
iss(Issuser):代表这个JWT的签发主体;
sub(Subject):代表这个JWT的主体,即它的所有人;
aud(Audience):代表这个JWT的接收对象;
exp(Expiration time):是一个时间戳,代表这个JWT的过期时间;
nbf(Not Before):是一个时间戳,代表这个JWT生效的开始时间,意味着在这个时间之前验证JWT是会失败的;
iat(Issued at):是一个时间戳,代表这个JWT的签发时间;
jti(JWT ID):是JWT的唯一标识。
第三部分即签名,签名由生成该字符串的服务端生成。生成的过程是将前两部分的Base64url编码后的内容通过点拼接起来,然后对其进行HS256进行加密,再进行base64编码,得到第三部分的内容。
base64url( HMACSHA256( base64UrlEncode(header) + "." + base64UrlEncode(payload), your-256-bit-secret (秘钥加盐) ) )
最后将三段字符串通过 .
拼接起来就生成了jwt的token。
了解了jwt的生成方式,即可以根据上述的方式来生成一个合法的jwt字符串,而这个功能已经有现成的模块实现,即pyjwt模块,使用该模块即可简单生成jwt字符串和验证jwt,如果需要了解具体过程,查看pyjwt源码即可。
pip3 install pyjwt
import jwt import datetime from jwt import exceptions SALT = ‘iv%x6xo7l7_u9bf_u!9#g#m*)*=ej@bek5)(@u3kh*72+unjv=‘ def create_token(): # 第一部分内容,构造header headers = { ‘typ‘: ‘jwt‘, ‘alg‘: ‘HS256‘ } # 第二部分内容 构造payload payload = { ‘user_id‘: 1, # 自定义用户ID ‘username‘: ‘wupeiqi‘, # 自定义用户名 ‘exp‘: datetime.datetime.utcnow() + datetime.timedelta(minutes=5) # 超时时间 }
# 第三部分,使用前两部分内容,加盐(SALT),并进行BASEurl64编码得到jwt result = jwt.encode(payload=payload, key=SALT, algorithm="HS256", headers=headers).decode(‘utf-8‘) return result if __name__ == ‘__main__‘: token = create_token() print(token)
服务器得到用户的jwt后,需要判断该数据的合法性,通过验证签名,确保数据没有被篡改。同时验证是否过期,验证成功后,从第二部分中提取内容即可。
详细过程:
header
、payload
、crypto
三部分。对第一部分header
进行base64url解密,得到header
对第二部分payload
进行base64url解密,得到payload
对第三部分crypto
进行base64url解密,得到signature
验证第三部分signature
部分数据
HS256
signature
密文进行比较。如果相同,则校验通过import jwt import datetime from jwt import exceptions def get_payload(token): """ 根据token获取payload :param token: :return: """ try: # 从token中获取payload【不校验合法性】 # unverified_payload = jwt.decode(token, None, False) # print(unverified_payload) # 从token中获取payload【校验合法性】 verified_payload = jwt.decode(token, SALT, True) return verified_payload except exceptions.ExpiredSignatureError: print(‘token已失效‘) except jwt.DecodeError: print(‘token认证失败‘) except jwt.InvalidTokenError: print(‘非法的token‘) if __name__ == ‘__main__‘: token = "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJleHAiOjE1NzM1NTU1NzksInVzZXJuYW1lIjoid3VwZWlxaSIsInVzZXJfaWQiOjF9.xj-7qSts6Yg5Ui55-aUOHJS4KSaeLq5weXMui2IIEJU" payload = get_payload(token)
通过jwt模块的decode方法将会自动对jwt的过期时间,数据的合法性等等进行检验(需要指定特定的参数),如果校验通过则返回payload数据。否则抛出异常。得到payload数据后,将这个字符串解析为的字典格式就能方便的提取内部的内容了。
原文:https://www.cnblogs.com/k5210202/p/13394179.html