测试开发进阶(三十一)

测试开发进阶(三十一)

用户模块

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

1
$ pip install djangorestframework-jwt

使用

setting.py中添加

1
2
3
4
5
6
7
8
9
10
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

1
2
3
4
5
6
from django.urls import path
from rest_framework_jwt.views import obtain_jwt_token

urlpatterns = [
path('login/', obtain_jwt_token),
]

主路由:

ApiTest/urls.py

1
2
3
urlpatterns = [
path('users/', include('user.urls'))
]

不登录的访问

不登录的访问

登录后的返回内容

登录

获取到token

1
2
3
4
5
6
7
8
HTTP 200 OK
Allow: POST, OPTIONS
Content-Type: application/json
Vary: Accept

{
"token": "eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJ1c2VyX2lkIjoxLCJ1c2VybmFtZSI6Inpob25neGluIiwiZXhwIjoxNTcyMzA5MzQ2LCJlbWFpbCI6IjQ5MDMzNjUzNEBxcS5jb20ifQ.ZDEeBAgSuPyvh1KBnF1sSY9w22guSRHXm8sbgqEWusg"
}
1
2
3
import base64
base64.b64decode('eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9')
# b'{"typ":"JWT","alg":"HS256"}'

认证过期时间

./site-packages/rest_framework_jwt/settings.py

第40行

1
'JWT_EXPIRATION_DELTA': datetime.timedelta(seconds=300),

Token默认过期时间5分钟

自行修改过期时间ApiTest/settings.py

过期时间为1天

1
2
3
JWT_AUTH = {
'JWT_EXPIRATION_DELTA': datetime.timedelta(days=1)
}

请求Token 头

1
'JWT_AUTH_HEADER_PREFIX': 'JWT',

使用httpie发起包含token的内容

安装httpie-jwt-auth插件

1
2
3
$ export JWT_AUTH_TOKEN='你的token'
$ export JWT_AUTH_PREFIX='JWT'
$ http -A jwt :8000/projects/ page==2 size==2

修改载荷

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
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重写

1
2
3
4
5
6
def jwt_response_payload_handler(token, user=None, request=None):
return {
'token': token,
'user_id': user.id,
'username': user.username,
}
1
2
3
4
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

注册的APP

数据库中的用户部分

注册

  • 用户名(6-20位,不重复)
  • 邮箱(符合邮箱格式)
  • 密码(6-20位,和确认密码一致)
  • 确认密码(6-20位,和密码一致)

user/serializers.py

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
from rest_framework import serializers
from django.contrib.auth.models import User


class 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

1
2
3
4
5
6
7
8
from django.urls import path
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

1
2
3
4
5
6
7
8
9
from rest_framework.generics import CreateAPIView
from rest_framework_jwt.authentication import JSONWebTokenAuthentication

from user.serializers import RegisterSerializer


class RegisterView(CreateAPIView):
serializer_class = RegisterSerializer
authentication_classes = (JSONWebTokenAuthentication,)
 wechat
欢迎您扫一扫上面的微信公众号,订阅我的博客!
您的支持将鼓励我继续创作!