基于rest_framework的jwt认证

1 认证原理

1 jwt分三段式:头.体.签名 (head.payload.sgin2 头和体是可逆加密,让服务器可以反解出user对象;签名是不可逆加密,保证整个token的安全性的
3 头体签名三部分,都是采用json格式的字符串,进行加密,可逆加密一般采用base64算法,不可逆加密一般采用hash(md5)算法
4 头中的内容是基本信息:公司信息、项目组信息、token采用的加密方式信息
{
	"company": "公司信息",
	...
}
5 体中的内容是关键信息:用户主键、用户名、签发时客户端信息(设备号、地址)、过期时间
{
	"user_id": 1,
	...
}
6 签名中的内容时安全信息:头的加密结果 + 体的加密结果 + 服务器不对外公开的安全码 进行md5加密
{
	"head": "头的加密字符串",
	"payload": "体的加密字符串",
	"secret_key": "安全码"
}

2 校验

1 将token按 . 拆分为三段字符串,第一段 头加密字符串 一般不需要做任何处理
2 第二段 体加密字符串,要反解出用户主键,通过主键从User表中就能得到登录用户,过期时间和设备信息都是安全信息,确保token没过期,且时同一设备来的
3 再用 第一段 + 第二段 + 服务器安全码 不可逆md5加密,与第三段 签名字符串 进行碰撞校验,通过后才能代表第二段校验得到的user对象就是合法的登录用户

3 认证流程

1 用账号密码访问登录接口,登录接口逻辑中调用 签发token 算法,得到token,返回给客户端,客户端自己存到cookies中

2 校验token的算法应该写在认证类中(在认证类中调用),全局配置给认证组件,所有视图类请求,都会进行认证校验,所以请求带了token,就会反解出user对象,在视图类中用request.user就能访问登录的用户

注:登录接口需要做 认证 + 权限 两个局部禁用

4 jwt拓展的安装

pip install djangorestframework-jwt

5 token的获取

 #urls.py
	from rest_framework_jwt.views import ObtainJSONWebToken,VerifyJSONWebToken,RefreshJSONWebToken,obtain_jwt_token
	path('login/', obtain_jwt_token)  # obtain_jwt_token和ObtainJSONWebToken.as_view()相同,里面有post方法,返回签发token

利用此方法返回的数据只有token

6 控制登录接口返回的数据格式

	-第一种方案,自己写登录接口
	-第二种写法,用内置,控制登录接口返回的数据格式
		-jwt的配置信息中有这个属性
			'JWT_RESPONSE_PAYLOAD_HANDLER':
	'rest_framework_jwt.utils.jwt_response_payload_handler',
		-重写jwt_response_payload_handler,配置成咱们自己的,内置的返回一个只有token的字典,我们自定义可以再往里面加内容

7 自定制auth认证类

from rest_framework_jwt.authentication import BaseAuthentication,BaseJSONWebTokenAuthentication
from rest_framework.exceptions import AuthenticationFailed
from rest_framework_jwt.authentication import jwt_decode_handler
from rest_framework_jwt.authentication import get_authorization_header,
from rest_framework import exceptions
class MyJwtAuthentication(BaseJSONWebTokenAuthentication):
	def authenticate(self, request):
		jwt_value=request.META.get('HTTP_AUTHORIZATION')
		if jwt_value:
			try:
			#jwt提供了通过三段token,取出payload的方法,并且有校验功能
				payload=jwt_decode_handler(jwt_value)
			except jwt.ExpiredSignature:
				raise AuthenticationFailed('签名过期')
			except jwt.InvalidTokenError:
				raise AuthenticationFailed('用户非法')
			except Exception as e:
				# 所有异常都会走到这
				raise AuthenticationFailed(str(e))
			user=self.authenticate_credentials(payload)
			return user,jwt_value
		# 没有值,直接抛异常
		raise AuthenticationFailed('您没有携带认证信息')

8 手动签发token

# 使用用户名,手机号,邮箱,都可以登录#
# 前端需要传的数据格式
{
"username":"wjw/1332323223/33@qq.com",
"password":"lqz12345"
}
# 视图
from rest_framework.views import APIView
from rest_framework.viewsets import ViewSetMixin, ViewSet

from app02 import ser
class Login2View(ViewSet):  # 跟上面完全一样
	def login(self, request, *args, **kwargs):
		# 1 需要 有个序列化的类
		login_ser = ser.LoginModelSerializer(data=request.data,context={'request':request})
		# 2 生成序列化类对象
		# 3 调用序列号对象的is_validad
		login_ser.is_valid(raise_exception=True)
		token=login_ser.context.get('token')
		# 4 return
		return Response({'status':100,'msg':'登录成功','token':token,'username':login_ser.context.get('username')})

# 序列化类
from rest_framework import serializers
from api import models
import re
from rest_framework.exceptions import ValidationError

from rest_framework_jwt.utils import jwt_encode_handler,jwt_payload_handler
class LoginModelSerializer(serializers.ModelSerializer):
	username=serializers.CharField()  # 重新覆盖username字段,数据中它是unique,post,认为你保存数据,自己有校验没过
	class Meta:
		model=models.User
		fields=['username','password']

	def validate(self, attrs):

		print(self.context)

		# 在这写逻辑
		username=attrs.get('username') # 用户名有三种方式
		password=attrs.get('password')
		# 通过判断,username数据不同,查询字段不一样
		# 正则匹配,如果是手机号
		if re.match('^1[3-9][0-9]{9}$',username):
			user=models.User.objects.filter(mobile=username).first()
		elif re.match('^.+@.+$',username):# 邮箱
			user=models.User.objects.filter(email=username).first()
		else:
			user=models.User.objects.filter(username=username).first()
		if user: # 存在用户
			# 校验密码,因为是密文,要用check_password
			if user.check_password(password):
				# 签发token
				payload = jwt_payload_handler(user)  # 把user传入,得到payload
				token = jwt_encode_handler(payload)  # 把payload传入,得到token
				self.context['token']=token
				self.context['username']=user.username
				return attrs
			else:
				raise ValidationError('密码错误')
		else:
			raise ValidationError('用户不存在')

9 配置setting修改token过期时间

import datetime
JWT_AUTH={
	'JWT_RESPONSE_PAYLOAD_HANDLER':'app02.utils.my_jwt_response_payload_handler',
	'JWT_EXPIRATION_DELTA': datetime.timedelta(days=7), # 过期时间,手动配置
}
posted @   mress  阅读(408)  评论(0编辑  收藏  举报
相关博文:
阅读排行:
· 被坑几百块钱后,我竟然真的恢复了删除的微信聊天记录!
· 没有Manus邀请码?试试免邀请码的MGX或者开源的OpenManus吧
· 【自荐】一款简洁、开源的在线白板工具 Drawnix
· 园子的第一款AI主题卫衣上架——"HELLO! HOW CAN I ASSIST YOU TODAY
· Docker 太简单,K8s 太复杂?w7panel 让容器管理更轻松!
点击右上角即可分享
微信分享提示