Json Web Token认证
最常见的认证机制
Session认证
Token认证
Session认证
保持在服务端,增加服务器开销
分布式架构中,难以维持Session会话同步
CSRF攻击风险(跨站请求)
Token认证
保存在客户端
跨语言,跨平台
扩展性强
鉴权性能高
JWT(Json Web Token)
由三部分组成
header
声明类型
声明加密算法
base64加密,可以解密
playload
存放过期时间,签发用户等
可以添加用户的非敏感信息
base64加密,可以解密
signature
由三部分组成
使用base64加密之后的header + . + 使用base64加密之后的playload + 使用HS256算法加密,同时secret加盐处理
安装djangorestframework-jwt
pip install djangorestframework-jwt
在 setting.py中添加
REST_FRAMEWORK = {
# 默认响应渲染类
'DEFAULT_RENDERER_CLASSES': (
# json渲染器为第一优先级
'rest_framework.renderers.JSONRenderer',
# 可浏览的API渲染器为第二优先级
'rest_framework.renderers.BrowsableAPIRenderer',
),
'DEFAULT_FILTER_BACKENDS': ['rest_framework.filters.OrderingFilter',
'django_filters.rest_framework.backends.DjangoFilterBackend'],
# 在全局指定分页的引擎
#'DEFAULT_PAGINATION_CLASS': 'rest_framework.pagination.PageNumberPagination',
'DEFAULT_PAGINATION_CLASS': 'utils.pagination.PageNumberPaginationManual',
# 同时必须指定每页显示的条数
# 'PAGE_SIZE': 3,
'DEFAULT_AUTHENTICATION_CLASSES': [
# 使用JWT Token认证
'rest_framework_jwt.authentication.JSONWebTokenAuthentication',
# Basic类型的认证(账号和密码)
'rest_framework.authentication.SessionAuthentication',
# Session会话认证
'rest_framework.authentication.BasicAuthentication'
],
}
添加路由
user/urls.py
from django.urls import path, include
from rest_framework_jwt.views import obtain_jwt_token
from . import views
urlpatterns = [
path('login/', obtain_jwt_token),
path('register/', views.RegisterView.as_view()),
]
主路由添加
urlpatterns = [
path('user/', include('user.urls')),]
登录成功
Token 第一段header 和 第二段playload載荷解密
注意第二段解密的时候如果位数不足需要在最后面添加一个或者两个 “=”
import base64
token = "eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJ1c2VyX2lkIjoyLCJ1c2VybmFtZSI6ImxpdXdlaTExIiwiZXhwIjoxNjAyODEwMTQxLCJlbWFpbCI6IjU4OUBxcS5jb20ifQ.sM2LIPmUL7jvOBx1dO4eutVgb3bt1vcu1AVruOgGXtY"
a = base64.b64decode('eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9')
print(a) #b'{"typ":"JWT","alg":"HS256"}'
b = base64.b64decode('eyJ1c2VyX2lkIjoyLCJ1c2VybmFtZSI6ImxpdXdlaTExIiwiZXhwIjoxNjAyODEwMTQxLCJlbWFpbCI6IjU4OUBxcS5jb20ifQ==')
print(b)#b'{"user_id":2,"username":"liuwei11","exp":1602810141,"email":"589@qq.com"}'
认证过期时间的修改
./site-packages/rest_framework_jwt/settings.py 四十行代码
'JWT_EXPIRATION_DELTA':datetime.timedelta(seconds=300),#默认300s
在项目setting中修改,token时效,认证形式(默认JWT开头),登录成功后的返回内容(载客palyload)
JWT_AUTH = {
# 默认5分钟过期, 可以使用JWT_EXPIRATION_DELTA来设置过期时间
'JWT_EXPIRATION_DELTA': datetime.timedelta(days=1),
# 'JWT_AUTH_HEADER_PREFIX': 'B',#M默认是JWT
'JWT_RESPONSE_PAYLOAD_HANDLER':
'utils.jwt_handler.jwt_response_payload_handler',
}
playload源码只返回token(rest_framework_jwt.utils.jwt_response_payload_handler)
def jwt_response_payload_handler(token, user=None, request=None):
"""
Returns the response data for both the login and refresh views.
Override to return a custom response such as including the
serialized representation of the User.
Example:
def jwt_response_payload_handler(token, user=None, request=None):
return {
'token': token,
'user': UserSerializer(user, context={'request': request}).data
}
"""
return {
'token': token
}
重写jwt_response_payload_handler,在项目的setting中JWT_AUTH指定(见上面):
def jwt_response_payload_handler(token, user=None, request=None):
return {
'token': token,
'user_id': user.id,
'username': user.username
}
Django自带的用户模型
django.contrib.auth.models.User
查看settings.py可以发现,默认注册了 django.contrib.auth,User的model就在这里面
**
注册
**
用户名(6-20位,不重复)
邮箱(符合邮箱格式)
密码(6-20位,和确认密码一致)
确认密码(6-20位,和密码一致
user/serializers.py
from rest_framework import serializers
from django.contrib.auth.models import User
from rest_framework_jwt.settings import api_settings
class RegisterSerializer(serializers.ModelSerializer):
password_confirm = serializers.CharField(label='确认密码', help_text='确认密码',
min_length=6, max_length=20,
write_only=True,
error_messages={
'min_length': '仅允许6~20个字符的确认密码',
'max_length': '仅允许6~20个字符的确认密码', })
token = serializers.CharField(label='生成token', read_only=True)
#因为User 的model中没有password_confirm 和token 所以要在这上面指定校验
class Meta:
model = User
fields = ('id', 'username', 'password', 'email', 'password_confirm', 'token')
extra_kwargs = {
'username': {
'label': '用户名',
'help_text': '用户名',
'min_length': 6,
'max_length': 20,
'error_messages': {
'min_length': '仅允许6-20个字符的用户名',
'max_length': '仅允许6-20个字符的用户名',
}
},
'email': {
'label': '邮箱',
'help_text': '邮箱',
'write_only': True,
'required': True,
},
'password': {
'label': '密码',
'help_text': '密码',
'write_only': True,
'min_length': 6,
'max_length': 20,
'error_messages': {
'min_length': '仅允许6-20个字符的密码',
'max_length': '仅允许6-20个字符的密码',
}
}
}
def validate(self, attrs):
password = attrs.get('password')
password_confirm = attrs.get('password_confirm')
if password != password_confirm:
raise serializers.ValidationError('两次输入的密码不正确')
return attrs
def create(self, validated_data):
validated_data.pop('password_confirm')
user = User.objects.create_user(**validated_data)
# 创建手动创建token,注册成功之后返回token
jwt_payload_handler = api_settings.JWT_PAYLOAD_HANDLER
jwt_encode_handler = api_settings.JWT_ENCODE_HANDLER
payload = jwt_payload_handler(user)
token = jwt_encode_handler(payload)
user.token = token
return user
user/urls.py
from django.urls import path, include
from rest_framework_jwt.views import obtain_jwt_token
from . import views
urlpatterns = [
path('login/', obtain_jwt_token),
path('register/', views.RegisterView.as_view()),
]
user/views.py
from rest_framework.generics import CreateAPIView
from . import serializers
class RegisterView(CreateAPIView):
serializer_class = serializers.RegisterSerializer
# authentication_classes = (JSONWebTokenAuthentication, )