jwt认证
一 cookie,session和token介绍
https://www.cnblogs.com/liuqingzheng/p/8990027.html
cookie:是存储在客户端的键值对。
session:是存储在服务端的键值对。
token:是三段式,由服务端生成,返回给给客户端(浏览器存储在cookie中,移动端存储在硬盘中),服务端不存储。
django中的session认证机制
二 jwt原理介绍
JWT就是一段字符串,由三段信息构成的,将这三段信息文本用.链接一起就构成了Jwt字符串。就像这样:
"eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.
eyJ1c2VyX2lkIjoxLCJ1c2VybmFtZSI6Inh1eGlhb3h1IiwiZXhwIjoxNjY1NTYxNjk0LCJlbWFpbCI6IjNAcXEuY29tIn0.
BtO6Ls2vSdrsJHkjZzkzmJ71Ue6zEIBkHV_iEmoZfEI"
第一部分是头部(header),第二部分是荷载(payload),第三部分是签名(signature)。
2.1 header
{
"typ":"JWT",
"alg":"HS256"
}
头部包含两部分信息,typ是类型,alg是加密算法。
然后将头部进行base64编码(该编码是可以对称解码的),构成了第一部分.
eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9
2.2 payload
{
"exp": "1234567890",
"name": "John Doe",
"userid": 3
}
iss: jwt签发者
sub: jwt所面向的用户
aud: 接收jwt的一方
exp: jwt的过期时间,这个过期时间必须要大于签发时间
nbf: 定义在什么时间之前,该jwt都是不可用的.
iat: jwt的签发时间
jti: jwt的唯一身份标识,主要用来作为一次性token,从而回避时序攻击。
对其进行编码得到
eyJ1c2VyX2lkIjoyLCJ1c2VybmFtZSI6Imphc3BlciIsImV4cCI6MTY2NTU2Mjc0MywiZW1haWwiOiIyQHFxLmNvbSJ9
2.3 signature
把编码后的header和编码后的payload使用.连接起来,加密后得到的结果。
BtO6Ls2vSdrsJHkjZzkzmJ71Ue6zEIBkHV_iEmoZfEI
django中token机制
三 base64编码与解码
在jwt、网络中传输字符串、网络中传输图片都可以使用base64编码,base64可以反解。
import base64
import json
header = json.dumps({
'typ': 'JWT',
'alg': 'HS256'
})
# 编码
res = base64.b64encode(header.encode(encoding='utf8'))
print(res) # b'eyJ0eXAiOiAiSldUIiwgImFsZyI6ICJIUzI1NiJ9'
# 解码
res1 = base64.b64decode(res)
print(res1) # b'{"typ": "JWT", "alg": "HS256"}'
print(res1.decode(encoding='utf8')) # {"typ": "JWT", "alg": "HS256"}
四 rest_framework_jwt快速使用
- 安装
pip3 install djangorestframework-jwt
- 基于auth_user表签发token,将数据库迁移。
- 创建用户。
- 登录接口已经写好了,只用带上参数发送post请求就可以签发了。
- 配置路由
from rest_framework_jwt.views import obtain_jwt_token urlpatterns = [ path('admin/', admin.site.urls), path('login/', obtain_jwt_token), ]
- 访问http://127.0.0.1:8000/login/
- 认证:在视图类中配置认证类和权限类
from rest_framework.views import APIView from rest_framework_jwt.authentication import JSONWebTokenAuthentication from rest_framework.permissions import IsAuthenticated from rest_framework.response import Response class UserView(APIView): authentication_classes = [JSONWebTokenAuthentication] permission_classes = [IsAuthenticated] def get(self, request): return Response('ok')
请求头中必须携带Authorization:jwt token串
五 jwt自定义返回数据格式
自定义一个函数
utils.py
def jwt_response_payload_handler(token, user=None, request=None):
return {
'code': 100,
'msg': '登陆成功',
'user': user.username,
'token': token
}
配置配置文件
settings.py
JWT_AUTH = {
'JWT_RESPONSE_PAYLOAD_HANDLER':
'app01.utils.jwt_response_payload_handler',
}
六 自定义user表,签发token
from rest_framework.views import APIView
from rest_framework.response import Response
from .models import User
from rest_framework.exceptions import ValidationError
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 UserView(APIView):
def post(self, request):
try:
username = request.data.get('username')
password = request.data.get('password')
user = User.objects.get(username=username, password=password)
if user:
payload = jwt_payload_handler(user)
token = jwt_encode_handler(payload)
print(payload, token)
return Response({
'code': 100,
'msg': '登录成功',
'user': payload.get('username'),
'token': token}
)
except Exception as e:
raise ValidationError(e)
七 自定义认证类,验证token
utils.py
class MyJSONWebTokenAuthentication(JSONWebTokenAuthentication):
def authenticate(self, request):
jwt_value = self.get_jwt_value(request)
if jwt_value is None:
return None
try:
payload = jwt_decode_handler(jwt_value)
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()
user = self.authenticate_credentials(payload)
return (user, jwt_value)
views.py
from rest_framework.views import APIView
from rest_framework.permissions import IsAuthenticated
from rest_framework.response import Response
from .models import User
from rest_framework.exceptions import ValidationError
from .utils import MyJSONWebTokenAuthentication
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 UserView(APIView):
def post(self, request):
try:
username = request.data.get('username')
password = request.data.get('password')
user = User.objects.get(username=username, password=password)
if user:
payload = jwt_payload_handler(user)
token = jwt_encode_handler(payload)
print(payload, token)
return Response({
'code': 100,
'msg': '登录成功',
'user': payload.get('username'),
'token': token}
)
except Exception as e:
raise ValidationError(e)
class User2View(APIView):
authentication_classes = [MyJSONWebTokenAuthentication]
permission_classes = [IsAuthenticated]
def get(self, request):
return Response('ok')
urls.py
urlpatterns = [
path('admin/', admin.site.urls),
path('login/', views.UserView.as_view()),
path('user/', views.User2View.as_view()),
]
八 自定义签发token,校验token
views.py
from rest_framework.exceptions import APIException
from rest_framework.response import Response
from rest_framework.views import APIView
from drf_day10 import settings
from .utils import MyJSONWebTokenAuthentication
from .models import User
import time
import json
import base64
import hashlib
class UserView(APIView):
def post(self, request):
try:
username = request.data.get('username')
password = request.data.get('password')
user = User.objects.get(username=username, password=password)
except Exception as e:
raise APIException(e)
user_id = user.id
username = user.username
exp = int(time.time() + 300)
header = base64.b64encode(json.dumps({"typ": "JWT", "alg": "md5"}).encode(encoding='utf8'))
payload = base64.b64encode(
json.dumps({'user_id': user_id, 'username': username, 'exp': exp}).encode(encoding='utf8'))
signature = header + b'.' + payload
md5 = hashlib.md5()
md5.update(signature)
# SECRET是在配置文件中配置的密钥(盐)
md5.update(settings.SECRET.encode(encoding='utf8'))
token = (header + b'.' + payload).decode(encoding='utf8') + '.' + md5.hexdigest()
return Response({
'code': 100,
'msg': '登录成功',
'user': json.loads(base64.b64decode(payload)).get('username'),
'token': token}
)
class User2View(APIView):
authentication_classes = [MyJSONWebTokenAuthentication]
def get(self, request):
return Response('ok')
utils.py
from rest_framework.exceptions import AuthenticationFailed
from rest_framework.authentication import BaseAuthentication
from drf_day10 import settings
from .models import User
import base64
import json
import time
import hashlib
class MyJSONWebTokenAuthentication(BaseAuthentication):
def authenticate(self, request):
try:
jwt_value = request.META.get('HTTP_AUTHORIZATION', b'')
left, right = jwt_value.split(' ')
header, payload, signature = right.split('.')
header = json.loads(base64.b64decode(header.encode(encoding='utf8')))
payload = json.loads(base64.b64decode(payload.encode(encoding='utf8')))
exe = payload.get('exp')
except:
raise AuthenticationFailed('错误解码签名')
if exe < int(time.time()):
raise AuthenticationFailed('令牌已过期')
re_signature = base64.b64encode(json.dumps(header).encode(encoding='utf8')) + b'.' + base64.b64encode(
json.dumps(payload).encode(encoding='utf8'))
md5 = hashlib.md5()
md5.update(re_signature)
md5.update(settings.SECRET.encode(encoding='utf8'))
re_signature = md5.hexdigest()
if signature != re_signature:
raise AuthenticationFailed('令牌校验失败')
user = User.objects.filter(id=payload.get('user_id')).first()
return user, None
urls.py
urlpatterns = [
path('login/', views.UserView.as_view()),
path('user/', views.User2View.as_view()),
]
补充知识
请求头中:X_FORWARDED_FOR 是什么?
1 没有使用代理服务器的情况:
REMOTE_ADDR = 您的 IP
HTTP_VIA = 没数值或不显示
HTTP_X_FORWARDED_FOR = 没数值或不显示
2 使用透明代理服务器的情况:Transparent Proxies
REMOTE_ADDR = 最后一个代理服务器 IP
HTTP_VIA = 代理服务器 IP
HTTP_X_FORWARDED_FOR = 您的真实 IP ,经过多个代理服务器时,这个值类似如下:203.98.182.163, 203.98.182.163, 203.129.72.215。
这类代理服务器还是将您的信息转发给您的访问对象,无法达到隐藏真实身份的目的。
3 使用普通匿名代理服务器的情况:Anonymous Proxies
REMOTE_ADDR = 最后一个代理服务器 IP
HTTP_VIA = 代理服务器 IP
HTTP_X_FORWARDED_FOR = 代理服务器 IP ,经过多个代理服务器时,这个值类似如下:203.98.182.163, 203.98.182.163, 203.129.72.215。
隐藏了您的真实IP,但是向访问对象透露了您是使用代理服务器访问他们的。
4 使用欺骗性代理服务器的情况:Distorting Proxies
REMOTE_ADDR = 代理服务器 IP
HTTP_VIA = 代理服务器 IP
HTTP_X_FORWARDED_FOR = 随机的 IP ,经过多个代理服务器时,这个值类似如下:203.98.182.163, 203.98.182.163, 203.129.72.215。
告诉了访问对象您使用了代理服务器,但编造了一个虚假的随机IP代替您的真实IP欺骗它。
5 使用高匿名代理服务器的情况:High Anonymity Proxies (Elite proxies)
REMOTE_ADDR = 代理服务器 IP
HTTP_VIA = 没数值或不显示
HTTP_X_FORWARDED_FOR = 没数值或不显示 ,经过多个代理服务器时,这个值类似如下:203.98.182.163, 203.98.182.163, 203.129.72.215。
完全用代理服务器的信息替代了您的所有信息,就象您就是完全使用那台代理服务器直接访问对象。