前言
-
本套代码借鉴其他博主,大家可去该博主的博客详细了解一下jwt实现流程和具体表现,本文只是做了某些代码的删减。
-
http本身是无状态的,所以无法设置jwt刷新和清除操作,违背了jwt的初衷,但业务需要实现某些功能,比如:退出登录,保持登录有效期等操作时。
就得借助数据库进行存储,在业务逻辑上进行数据库的操作实现退出登录和刷新token。(以上个人理解,如有不同见解可以提出互相讨论。)
代码实现
- 首先我们在项目目录下新建
utils/jwt_auth.py
文件,编写两个函数"生成token"和"校验token"
import jwt
import datetime
from jwt import exceptions
from mysite.settings import SECRET_KEY # 配置文件的加密字符串
from rest_framework import exceptions as es
def create_token(payload, timeout=20):
"""
生成token
:param payload: 例如:{'user_id':1,'username':'wupeiqi'}用户信息
:param timeout: token的过期时间,默认20分钟
:return:
"""
headers = {
'typ': 'jwt',
'alg': 'HS256'
}
# 对传进来的payload字典添加键值对,过期时间
payload['exp'] = datetime.datetime.utcnow() + datetime.timedelta(minutes=timeout)
# 加密
result = jwt.encode(payload=payload, key=SECRET_KEY, algorithm="HS256", headers=headers).decode('utf-8')
return result
def parse_payload(token):
"""
校验token并获取payload
:param token:
:return:
"""
try:
verified_payload = jwt.decode(token, SECRET_KEY, True)
except exceptions.ExpiredSignatureError:
raise es.AuthenticationFailed({'detail': 'token已失效'})
except jwt.DecodeError:
raise es.AuthenticationFailed({'detail': 'token认证失败'})
except jwt.InvalidTokenError:
raise es.AuthenticationFailed({'detail': '非法的token'})
return verified_payload
- 新建
utils/auth.py
文件,在该文件中编写我们自定义的认证组件,
必须继承restframework中的认证类BaseAuthentication
from rest_framework.authentication import BaseAuthentication
from rest_framework import exceptions
from polls.utils.jwt_auth import parse_payload
class JwtAuthorizationAuthentication(BaseAuthentication):
"""
用户需要通过请求头的方式来进行传输token
"""
def authenticate(self, request):
# 这里选择从请求头方式获取token
authorization = request.META.get('HTTP_AUTHORIZATION', '')
auth = authorization.split()
if not auth:
raise exceptions.AuthenticationFailed({'detail': '未获取到Authorization请求头'})
if auth[0].lower() != 'jwt':
raise exceptions.AuthenticationFailed({'detail': "Authorization请求头中认证方式错误"})
if len(auth) == 1:
raise exceptions.AuthenticationFailed({'detail': "非法Authorization请求头"})
elif len(auth) > 2:
raise exceptions.AuthenticationFailed({'detail': "非法Authorization请求头"})
token = auth[1]
result = parse_payload(token)
# 如果想要request.user等于用户对象,此处可以根据payload去数据库中获取用户对象。
return (result, token)
- 我们需要在
settings.py
全局配置文件中加入我们的认证组件作为全局认证
每条请求必须携带token并通过认证才能进入视图函数。
REST_FRAMEWORK = {
# 全局使用认证类
#注意里面是路径,将认证类写在utils文件夹的auth.py中,'api'填写你项目目录
"DEFAULT_AUTHENTICATION_CLASSES":['api.utils.auth.JwtAuthorizationAuthentication', ],
}
- 像我们的登录/注册是不需要认证的,我们可以在视图中屏蔽认证。
class LoginView(APIView):
authentication_classes = []
......