JWT原理和实战应用(django)
1、jwt
- json web token, 一般用于用户认证(前后端分离,微信小程序,app开发)
2、基于传统的token验证
- 用户登录。服务端给返回token,并将token保存在服务端。
- 之后用户在来访问时,需要携带token,服务端获取token后,再去数据库获取token进行校验。
from rest_framework.views import APIView
from rest_framework.response import Response
from APP import models
class ProLoginView(APIView):
'''用户登录'''
def post(self, request):
user = request.data.get('username')
password = request.data.get('password')
user_object = models.User.objects.filter(username=user, password=password).first()
if not user_object:
return Response({'code': 400, 'error': '用户名或密码错误'})
random_string = str(uuid.uuid4())
user_object.token = random_string
user_object.save()
return Response({'code': 200, 'data': random_string})
class OrderView(APIView):
"""token验证"""
def get(self, request, *args, **kwargs):
token = request.query_params.get('token')
if not token:
return Response({'code': 400, 'error': "登录成功后才能访问"})
user_object = models.User.objects.filter(token=token).first()
if not user_object:
return Response({'code': 400, 'errror': "token无效,请重新登录"})
return Response('订单列表')
3、jwt——token
- 用户登录,服务端给用户返回一个token(服务端不保存)。
- 之后用户再来访问,需要携带token,服务端获取token后,在做token校验。
- 优势:相较于传统token相比,他无需在服务端保存token。
4、jwt——token实现过程
- 提交用户密码给服务端,如果登陆成功,使用jwt创建一个token,并给用户返回。
eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiIxMjM0NTY3ODkwIiwibmFtZSI6IkpvaG4gRG9lIiwiaWF0IjoxNTE2MjM5MDIyfQ.SflKxwRJSMeKKF2QT4fwpMeJf36POk6yJV_adQssw5c
-
注意:jwt生成的token是三段字符串组成,并且连接起来。
- 第一段字符串,HEADER,内部包含算法/token类型。json转化成字符串,然后做 base64url 加密以及遇到’+_'做特殊字符串替换)
{ "alg": "HS256", # 算法 "typ": "JWT" # token类型 }
- 第二段字符串,payload,自定义值。json转化成字符串,然后做 base64url 加密以及遇到’+_'做特殊字符串替换)
{ "id": "1234567890", "name": "John Doe", "exp": 1516239022 # 超时时间 }
- 第三段字符串:
第一步:1,2两部分密文拼接起来 eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiIxMjM0NTY3ODkwIiwibmFtZSI6IkpvaG4gRG9lIiwiaWF0IjoxNTE2MjM5MDIyfQ 第二步:对前两部分密文进行HS256加密 + 加盐 第三步:对HS256加密后的密文在做base64url加密
-
之后用户再来访问,需要携带token,服务端对token进行校验。
- 获取token
- 第一步:对token进行切割
eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiIxMjM0NTY3ODkwIiwibmFtZSI6IkpvaG4gRG9lIiwiaWF0IjoxNTE2MjM5MDIyfQ.SflKxwRJSMeKKF2QT4fwpMeJf36POk6yJV_adQssw5c
- 第二步:对第二段进行base64url解密,并获取payload信息,检测token是否已经超时。
{ "id": "1234567890", "name": "John Doe", "exp": 1516239022 # 超时时间 }
- 第三步:把第1,2端拼接,再次执行HS256加密
第一步:1,2两部分密文拼接起来 eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiIxMjM0NTY3ODkwIiwibmFtZSI6IkpvaG4gRG9lIiwiaWF0IjoxNTE2MjM5MDIyfQ 第二步:对前两部分密文进行HS256加密 + 加盐 第三步:对HS256加密后的密文在做base64url加密(SflKxwRJSMeKKF2QT4fwpMeJf36POk6yJV_adQssw5c) 如果相等,表示token未被修改过(认证通过)
5、应用
基于Python的pyjwt模块创建jwt的token。
-
安装
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) # 超时时间 } result = jwt.encode(payload=payload, key=SALT, algorithm="HS256", headers=headers).decode('utf-8') return result if __name__ == '__main__': token = create_token() print(token)
6、jwt校验token
一般在认证成功后,把jwt生成的token返回给用户,以后用户再次访问时候需要携带token,此时jwt需要对token进行超时
及合法性
校验。
获取token之后,会按照以下步骤进行校验:
-
将token分割成
header_segment
、payload_segment
、crypto_segment
三部分jwt_token = "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiIxMjM0NTY3ODkwIiwibmFtZSI6IkpvaG4gRG9lIiwiaWF0IjoxNTE2MjM5MDIyfQ.SflKxwRJSMeKKF2QT4fwpMeJf36POk6yJV_adQssw5c" signing_input, crypto_segment = jwt_token.rsplit(b'.', 1) header_segment, payload_segment = signing_input.split(b'.', 1)
-
对第一部分
header_segment
进行base64url解密,得到header
-
对第二部分
payload_segment
进行base64url解密,得到payload
-
对第三部分
crypto_segment
进行base64url解密,得到signature
-
对第三部分
signature
部分数据进行合法性校验- 拼接前两段密文,即:
signing_input
- 从第一段明文中获取加密算法,默认:
HS256
- 使用 算法+盐 对
signing_input
进行加密,将得到的结果和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)
7、获取封装的数据
示例:
class JwtQueryParamsAuthentication(BaseAuthentication):
# 获取并判断token的合法性
def authenticate(self, request):
token = request.query_params.get('token')
salt = settings.SECRET_KEY
try:
payload = jwt.decode(token, salt, algorithms=['HS256'])
except exceptions.ExpiredSignatureError:
raise AuthenticationFailed({'code': 400, 'error': "token已失效,请重新登录"})
except jwt.DecodeError:
raise AuthenticationFailed({'code': 400, 'error': "token认证失败"})
except jwt.InvalidTokenError:
raise AuthenticationFailed({'code': 400, 'error': "非法的token"})
return (payload,token)
获取payload数据(示例):
class ProOrderView(APIView):
def get(self, request):
result = request.user
print(result.get('name'))
return Response(result)