测试开发进阶(三十一)

  • 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,)