测试开发进阶(三十一)
- 2019 年 11 月 5 日
- 筆記
用户模块
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_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 pathfrom rest_framework_jwt.views import obtain_jwt_tokenurlpatterns = [ path('login/', obtain_jwt_token),]
主路由:
ApiTest/urls.py
urlpatterns = [ path('users/', include('user.urls'))]
不登录的访问

登录后的返回内容


HTTP 200 OKAllow: POST, OPTIONSContent-Type: application/jsonVary: Accept{ "token": "eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJ1c2VyX2lkIjoxLCJ1c2VybmFtZSI6Inpob25neGluIiwiZXhwIjoxNTcyMzA5MzQ2LCJlbWFpbCI6IjQ5MDMzNjUzNEBxcS5jb20ifQ.ZDEeBAgSuPyvh1KBnF1sSY9w22guSRHXm8sbgqEWusg"}
import base64base64.b64decode('eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9')# b'{"typ":"JWT","alg":"HS256"}'
认证过期时间
./site-packages/rest_framework_jwt/settings.py
第40行
'JWT_EXPIRATION_DELTA': datetime.timedelta(seconds=300),
Token默认过期时间5分钟
自行修改过期时间 ApiTest/settings.py
过期时间为1天
JWT_AUTH = { 'JWT_EXPIRATION_DELTA': datetime.timedelta(days=1)}
请求Token 头
'JWT_AUTH_HEADER_PREFIX': 'JWT',
使用httpie发起包含token的内容
安装 httpie-jwt-auth
插件
$ export JWT_AUTH_TOKEN='你的token'$ export JWT_AUTH_PREFIX='JWT'$ http -A jwt :8000/projects/ page==2 size==2
修改载荷
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 }
在 utils/jwt_handler.py
重写
def jwt_response_payload_handler(token, user=None, request=None): return { 'token': token, 'user_id': user.id, 'username': user.username, }
JWT_AUTH = { 'JWT_EXPIRATION_DELTA': datetime.timedelta(days=1), 'JWT_PAYLOAD_GET_USERNAME_HANDLER': 'utils.jwt_handler.jwt_response_payload_handler'}
Django自带的用户模型
django.contrib.auth.models.User

查看settings.py可以发现,默认注册了 django.contrib.auth


注册
- 用户名(6-20位,不重复)
- 邮箱(符合邮箱格式)
- 密码(6-20位,和确认密码一致)
- 确认密码(6-20位,和密码一致)
user/serializers.py
from rest_framework import serializersfrom django.contrib.auth.models import Userclass RegisterSerializer(serializers.ModelSerializer): password_conform = serializers.CharField(label='确认密码', min_length=6, max_length=20, write_only=True, help_text='确认密码', error_messages={'min_length': '仅允许6~20个字符的确认密码', 'max_length': '仅允许6~20个字符的确认密码'} ) token = serializers.CharField(label='生成token', read_only=True) class Meta: model = User fields = ('id', 'username', 'password', 'email', 'password_conform', '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 create(self, validated_data): pass
user/urls.py
from django.urls import pathfrom rest_framework_jwt.views import obtain_jwt_tokenfrom . import viewsurlpatterns = [ path('login/', obtain_jwt_token), path('register/', views.RegisterView.as_view()),]
user/views.py
from rest_framework.generics import CreateAPIViewfrom rest_framework_jwt.authentication import JSONWebTokenAuthenticationfrom user.serializers import RegisterSerializerclass RegisterView(CreateAPIView): serializer_class = RegisterSerializer authentication_classes = (JSONWebTokenAuthentication,)