cookie,session,token介绍
-
cookie是:存在客户端浏览器的键值对
-
session是:存在于服务端的键值对
-
token是:三段式,服务端生成的,存放在客户端(浏览器就放在cookie中,移动端:存在移动端中)
-
使用token的认证机制,服务端还要存数据吗? token是服务的生成,客户端保存,服务的不存储token
token:三段式
第一段
头:公司信息,加密方式。。。 {}
第二段
荷载:真正的数据 {name:lqz,id:1}
第三段
签名,通过第一段和第二段,通过某种加密方式加密得到的 aafasdfas
- token的使用分两个阶段
- 登录成功后的【签发token阶段】--->生成三段
- 登录成功访问某个接口的 【验证阶段】--->验证token是否合法
JWT原理介绍
Json web token (JWT), token的应用于web方向的称之为jwt
-
构成和工作原理
- JWT就是一段字符串,由三段信息构成的,将这三段信息文本用.链接一起就构成了Jwt字符串 就像这样:
eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiIxMjM0NTY3ODkwIiwibmFtZSI6IkpvaG4gRG9lIiwiYWRtaW4iOnRydWV9.TJVA95OrM7E2cBab30RMHrHDcEfxjoYZgeFONFh7HgQ
-
header:头
- 声明类型 这里是jwt
- 声明加密的算法 通常直接使用 HMAC SHA256
- 公司信息
由
{
'typ': 'JWT',
'alg': 'HS256'
}
变成了(base64的编码)
eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9
-
payload:荷载
- exp: jwt的过期时间,这个过期时间必须要大于签发时间
- iat: jwt的签发时间
- 用户信息: 用户信息
由
{
"exp": "1234567890",
"name": "John Doe",
"userid": 3
}
变成了(base64的编码)
eyJzdWIiOiIxMjM0NTY3ODkwIiwibmFtZSI6IkpvaG4gRG9lIiwiYWRtaW4iOnRydWV9
-
signature:签名
把头(header)和荷载(payload)加密后得到的
TJVA95OrM7E2cBab30RMHrHDcEfxjoYZgeFONFh7HgQ
注意
ecret是保存在服务器端的(加密方式+盐),jwt的签发生成也是在服务器端的,secret就是用来进行jwt的签发和jwt的验证,所以,它就是你服务端的私钥,在任何场景都不应该流露出去。一旦客户端得知这个secret, 那就意味着客户端是可以自我签发jwt了
- jwt使用流程最核心的是
- 签发:登录接口签发
- 认证:认证类认证
base64编码与解码
base64 可以把字符串编码成base64的编码格式:(大小写字母,数字和 =号)
应用场景
- jwt中使用
- 网络中传输字符串就可以使用base64编码
- 网络中传输图片,也可能使用base64的编码
编码
import json
import base64
d = {'name': 'joker', 'age': 18}
info = json.dumps(d)
print(info) # {"name": "joker", "age": 18}
res = base64.b64encode(info.encode('utf8'))
print(res) # b'eyJuYW1lIjogImpva2VyIiwgImFnZSI6IDE4fQ=='
解码
import base64
s = b'eyJuYW1lIjogImpva2VyIiwgImFnZSI6IDE4fQ=='
res = base64.b64decode(s)
print(res) # {"name": "joker", "age": 18}
drf-jwt快速使用
jwt:签发(登录接口) 认证(认证类)
django中使用jwt 可以自己写
- https://github.com/jpadilla/django-rest-framework-jwt (比较老)
- https://github.com/jazzband/djangorestframework-simplejwt(比较新)
安装
pip3 install djangorestframework-jwt
快速使用
1.迁移表,因为它默认使用auth的user表签发token
2.创建超级用户(auth的user表中要右记录)
3.咱们不需要写登录接口了,如果是使用auth的user表作为用户表,它可以快速签发
签发(登录):只需要在路由中配置(因为它帮咱们写好登录接口了)
from rest_framework_jwt.views import obtain_jwt_token
from rest_framework_jwt.views import obtain_jwt_token
urlpatterns = [
path('login/', obtain_jwt_token),
]
认证(认证类):导入,配置在视图类上
from rest_framework.permissions import IsAuthenticated
from rest_framework_jwt.authentication import JSONWebTokenAuthentication
from rest_framework.views import APIView
from rest_framework.permissions import IsAuthenticated
from rest_framework_jwt.authentication import JSONWebTokenAuthentication
class TestView(APIView):
authentication_classes = [JSONWebTokenAuthentication,]
permission_classes = [IsAuthenticated,]
前端访问时,token需要放在请求头中
Authorization:jwt eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJ1c2VyX2lkIjoxLCJ1c2VybmFtZSI6Imphc29uIiwiZXhwIjoxNjY1NTYyMzkyfQ.3Xawf3tA-XGzTjUrXysKhWGt9HiRo8UE9iwuCrgOzew

drf-jwt修改返回格式
登录成功后,前端看到的格式,太固定了,只有token
{"token": "eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJ1c2VyX2lkIjoxLCJ1c2VybmFtZSI6Imphc29uIiwiZXhwIjoxNjY1NTYyMzkyfQ.3Xawf3tA-XGzTjUrXysKhWGt9HiRo8UE9iwuCrgOzew"}
我们想做成这样的
{
"code": 100,
"msg": "登录成功",
"token": "eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJ1c2VyX2lkIjoxLCJ1c2VybmFtZSI6Imphc29uIiwiZXhwIjoxNjY1NTYyMzkyfQ.3Xawf3tA-XGzTjUrXysKhWGt9HiRo8UE9iwuCrgOzew"
}
需要自己写一个函数
def jwt_response_payload_handler(token, user=None, request=None):
return {
'code':100,
'msg':'登录成功',
'username':user.username,
'token':token
}
把函数配置在配置文件中
以后登录接口返回的格式就是咱们写的函数的返回值
JWT_AUTH={
'JWT_RESPONSE_PAYLOAD_HANDLER': 'app01.response.jwt_response_payload_handler',
}
自定义user表,签发token
models.py 中定义UserInfo表
class UserInfo(models.Model):
username = models.CharField(max_length=32)
password = models.CharField(max_length=32)
写一个登录接口
from rest_framework.exceptions import APIException
from rest_framework_jwt.settings import api_settings
jwt_payload_handler = api_settings.JWT_PAYLOAD_HANDLER
jwt_encode_handler = api_settings.JWT_ENCODE_HANDLER
from .models import UserInfo
class UserView(APIView):
def post(self, request):
try:
username = request.data.get('username')
password = request.data.get('password')
user=UserInfo.objects.get(username=username,password=password)
# 根据user,签发token---》三部分:头,荷载,签名
# 使用djagnorestframework-jwt模块提供的签发token的函数,生成token
payload = jwt_payload_handler(user) # 通过user对象---》{username:lqz,id:1,过期时间}
token=jwt_encode_handler(payload) # 根据payload---》得到token:头.荷载.签名
return Response({'code':100,'msg':'登录成功','token':token})
except Exception as e:
raise APIException('用户名或密码错误')
自定义认证类
from rest_framework_jwt.authentication import BaseJSONWebTokenAuthentication
from rest_framework_jwt.settings import api_settings
from rest_framework import exceptions
import jwt
jwt_decode_handler = api_settings.JWT_DECODE_HANDLER
class JwtAuthentication(BaseJSONWebTokenAuthentication):
def authenticate(self, request):
token = request.META.get('HTTP_AUTHORIZATION')
# token = token[4:]
# print(token)
if token:
try:
payload = jwt_decode_handler(token)
print(payload)
except jwt.ExpiredSignature:
msg = '签名过期.'
raise exceptions.AuthenticationFailed(msg)
except jwt.DecodeError:
msg = '签名错误.'
raise exceptions.AuthenticationFailed(msg)
except jwt.InvalidTokenError:
raise exceptions.AuthenticationFailed()
# user = self.authenticate_credentials(payload) # 配合权限类使用
user = UserInfo.objects.filter(pk=payload.get('user_id')).first() # 不能配合权限类使用
return user, token
else:
raise exceptions.AuthenticationFailed('Authorization不能为空')
配置JWT时间
JWT_AUTH = {
'JWT_RESPONSE_PAYLOAD_HANDLER': 'app01.jwt.jwt_response_payload_handler',
'JWT_EXPIRATION_DELTA': datetime.timedelta(days=7), # JWT过期时间
}
'''
days: 天
seconds: 秒
microseconds: 微秒
milliseconds: 毫秒
minutes: 分钟
hours: 小时
weeks: 周
'''
posted on
浙公网安备 33010602011771号