JWT
# 2 JWT介绍(`****`)
jwt:json web token,是一种前后端的登录认证方式,它分token的签发和认证,签发的意思是用户登录成功,生成三段式的token串;认证指的是用户访问某个接口,需要携带token串过来,我们完成认证
三段:头.荷载.签名,每一段都通过base64编码
# Json web token (JWT), 是为了在网络应用环境间传递声明而执行的一种基于JSON的开放标准((RFC 7519).该token被设计为紧凑且安全的,特别适用于分布式站点的单点登录(SSO)场景。JWT的声明一般被用来在身份提供者和服务提供者间传递被认证的用户身份信息,以便于从资源服务器获取资源,也可以增加一些额外的其它业务逻辑所必须的声明信息,该token也可直接被用于认证,也可被加密
# JWT的构成
-三段式:
-header:一般放公司信息,加密方式 (用处不大)
-payload:用户信息: user_id,user_name,email,expire
-signature:第一部分和第二部加密得到的
-通过base64转码
eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiIxMjM0NTY3ODkwIiwibmFtZSI6IkpvaG4gRG9lIiwiYWRtaW4iOnRydWV9.TJVA95OrM7E2cBab30RMHrHDcEfxjoYZgeFONFh7HgQ
3 jwt快速使用(**)
# 使用第三方模块:django-rest-framework-jwt(作者好多年不改了)
# 新的,用起来,跟它差不多(名字忘了)
# 安装:pip install djangorestframework-jwt
# base64的使用(转码方式)
# 快速使用,签发token给前端
-用户表使用auth的user表
-登录功能不用写,djangorestframework-jwt帮你写好了
4 jwt内置认证类使用(**)
4.1 签发(直接使用内置的)
# 在路由中配置
from rest_framework_jwt.views import obtain_jwt_token
urlpatterns = [
path('login/', obtain_jwt_token), # 登录接口好了,可以签发token,依赖了auth的user表
]
4.2 认证
from rest_framework_jwt.authentication import JSONWebTokenAuthentication # drf_jwt内置的认证类
from rest_framework.permissions import IsAuthenticated
class BookView(APIView):
# 使用drf_jwt内置的认证,必须加这两个
authentication_classes = [JSONWebTokenAuthentication, ]
permission_classes = [IsAuthenticated]
1 jwt内置签发方法修改返回格式(***)
# 写一个函数
def jwt_response_payload_handler(token, user=None, request=None):
return {
'token':token,
'username':user.username
}
# 在配置文件中配置
#drf_jwt的配置文件(drf_jwt有个默认配置文件:from rest_framework_jwt import settings)
from rest_framework_jwt.utils import jwt_response_payload_handler
JWT_AUTH={
'JWT_RESPONSE_PAYLOAD_HANDLER':'app01.utils.jwt_response_payload_handler'
}
1.2 jwt签发的源码分析
# 路由的---》obtain_jwt_token---》ObtainJSONWebToken.as_view()---》视图类ObtainJSONWebToken----》
class ObtainJSONWebToken(JSONWebTokenAPIView):
# 序列化类
serializer_class = JSONWebTokenSerializer
# 登录请求,发了一个post请求,携带用户名密码---》ObtainJSONWebToken的post请求
# JSONWebTokenAPIView内有个post
def post(self, request, *args, **kwargs):
# 得到一个序列化类的对象,使用传入的request.data
serializer = self.get_serializer(data=request.data)
# 序列化类对象的is_valid,会执行字段自己的校验规则,局部钩子,全局钩子
if serializer.is_valid():
# 取出user,取出token
user = serializer.object.get('user') or request.user
token = serializer.object.get('token')
# 执行你在配置文件中配置的函数
response_data = jwt_response_payload_handler(token, user, request)
response = Response(response_data)
return response
# JSONWebTokenSerializer源码
-validate
def validate(self, attrs):
credentials = {
self.username_field: attrs.get(self.username_field),
'password': attrs.get('password')
}
# {username:lq,password:lqz12345}
if all(credentials.values()):
user = authenticate(**credentials) # 通过用户名密码认证auth的user表中的用户
if user:
# 通过user得到荷载{user_id:1,username:lqz,..}
payload = jwt_payload_handler(user)
return {
'token': jwt_encode_handler(payload), # 通过荷载得到token串
'user': user
}
1.3 drf_jwt认证token源码
# 1 读JSONWebTokenAuthentication的父类BaseJSONWebTokenAuthentication的authenticate
def authenticate(self, request):
# 取出前端传入的token串
jwt_value = self.get_jwt_value(request)
if jwt_value is None: # 如果token串,前端没有传,也认证通过,后续必须加权限类
return None
try:
payload = jwt_decode_handler(jwt_value) # 通过token串获得payload,验签(是否被篡改),检查过期时间
except jwt.ExpiredSignature:
msg = _('Signature has expired.')
raise exceptions.AuthenticationFailed(msg)
except jwt.DecodeError:
msg = _('Error decoding signature.')
raise exceptions.AuthenticationFailed(msg)
except jwt.InvalidTokenError:
raise exceptions.AuthenticationFailed()
# 通过payload获得当前用户:通过user_id---->auth的user表中获取当前用户
user = self.authenticate_credentials(payload)
return (user, jwt_value)
# 2 JSONWebTokenAuthentication的get_jwt_value方法,返回token串
def get_jwt_value(self, request):
# 取出前端传入的token串,通过空格切分
#(jwt,token串)
auth = get_authorization_header(request).split()
auth_header_prefix = api_settings.JWT_AUTH_HEADER_PREFIX.lower()
if not auth: # 如果没有传认证的token,返回None
if api_settings.JWT_AUTH_COOKIE:
return request.COOKIES.get(api_settings.JWT_AUTH_COOKIE)
return None
if smart_text(auth[0].lower()) != auth_header_prefix:
return None
if len(auth) == 1: # auth元组的长度如果等于1,抛异常,因为我们jwt token串
msg = _('Invalid Authorization header. No credentials provided.')
raise exceptions.AuthenticationFailed(msg)
elif len(auth) > 2: # auth元组的长度如果大于1,抛异常,因为我们jwt token串
msg = _('Invalid Authorization header. Credentials string '
'should not contain spaces.')
raise exceptions.AuthenticationFailed(msg)
return auth[1]
2 自定义User表实现jwt的token签发(****)
from rest_framework.response import Response
from .models import UserInfo
from rest_framework_jwt.settings import api_settings
jwt_payload_handler = api_settings.JWT_PAYLOAD_HANDLER
jwt_encode_handler = api_settings.JWT_ENCODE_HANDLER
class LoginView(APIView):
def post(self, request):
username = request.data.get('username')
password = request.data.get('password')
user = UserInfo.objects.filter(username=username, password=password).first()
if user: # 用户名和密码正确,登录成功,签发token
payload = jwt_payload_handler(user)
token = jwt_encode_handler(payload)
return Response({'code': '100', 'msg': '登录成功', 'token': token, 'usrename': user.username})
else:
return Response({'code': '101', 'msg': '用户名或密码错误'})
3 自定义认证类,实现jwt的token认证(****)
from rest_framework.authentication import BaseAuthentication
from rest_framework import exceptions
import jwt
from .models import UserInfo
from rest_framework_jwt.settings import api_settings
jwt_decode_handler = api_settings.JWT_DECODE_HANDLER
class JwtAuthentication(BaseAuthentication):
def authenticate(self, request):
# 取出前端传入的token串
token=request.META.get('HTTP_TOKEN')
# 通过token获得payload
try:
payload = jwt_decode_handler(token) # 通过token串获得payload,验签(是否被篡改),检查过期时间
# except Exception:
# raise exceptions.AuthenticationFailed('token认证失败')
except jwt.ExpiredSignature:
msg = '签名过期'
raise exceptions.AuthenticationFailed(msg)
except jwt.DecodeError:
msg ='解码错误'
raise exceptions.AuthenticationFailed(msg)
except jwt.InvalidTokenError:
raise exceptions.AuthenticationFailed()
# 通过payload获得当前用户(自己的表,自己拿)
# user=UserInfo.objects.filter(pk=payload['user_id']).first()
# 稍微优化一下,不是每次都取查询当前登录用户
# user={'id':payload['user_id'],'username':payload['username']}
user=UserInfo(id=payload['user_id'],username=payload['username'])
# 返回当前用户
return user,token

浙公网安备 33010602011771号