一:用户部分:

用户注册:

用户注册序列化器:

  1 import re
  2 
  3 from django_redis import get_redis_connection
  4 from rest_framework import serializers
  5 
  6 from users.models import User
  7 
  8 
  9 class CreateUserSerializer(serializers.ModelSerializer):
 10     """创建用户序列化器类"""
 11     password2 = serializers.CharField(label='重复密码', write_only=True)
 12     sms_code = serializers.CharField(label='短信验证码', write_only=True)
 13     allow = serializers.CharField(label='是否同意协议', write_only=True)
 14     token = serializers.CharField(label='JWT Token', read_only=True)
 15 
 16     class Meta:
 17         model = User
 18         fields = ('id', 'username', 'password', 'mobile', 'password2', 'sms_code', 'allow', 'token')
 19 
 20         extra_kwargs = {
 21             'username': {
 22                 'min_length': 5,
 23                 'max_length': 20,
 24                 'error_messages': {
 25                     'min_length': '仅允许5-20个字符的用户名',
 26                     'max_length': '仅允许5-20个字符的用户名',
 27                 }
 28             },
 29             'password': {
 30                 'write_only': True,
 31                 'min_length': 8,
 32                 'max_length': 20,
 33                 'error_messages': {
 34                     'min_length': '仅允许8-20个字符的密码',
 35                     'max_length': '仅允许8-20个字符的密码',
 36                 }
 37             }
 38         }
 39 
 40     # (参数完整性,手机号格式,手机号是否已注册,是否同意协议,两次密码是否一致,短信验证码是否正确)
 41     def validate_username(self, value):
 42         """用户名不能是纯数字"""
 43         if re.match(r'^\d+$', value):
 44             raise serializers.ValidationError('用户名不能都是数字')
 45 
 46         return value
 47 
 48     def validate_mobile(self, value):
 49         """手机号格式,手机号是否已注册"""
 50         # 手机号格式
 51         if not re.match(r'^1[3-9]\d{9}$', value):
 52             raise serializers.ValidationError('手机号格式不正确')
 53 
 54         # 手机号是否已注册
 55         count = User.objects.filter(mobile=value).count()
 56         if count > 0:
 57             raise serializers.ValidationError('手机号已存在')
 58 
 59         return value
 60 
 61     def validate_allow(self, value):
 62         """是否同意协议"""
 63         if value != 'true':
 64             raise serializers.ValidationError('请同意协议')
 65         return value
 66 
 67     def validate(self, attrs):
 68         """两次密码是否一致,短信验证码是否正确"""
 69         # 两次密码是否一致
 70         password = attrs['password']
 71         password2 = attrs['password2']
 72 
 73         if password != password2:
 74             raise serializers.ValidationError('两次密码不一致')
 75 
 76         # 短信验证码是否正确
 77         # 获取真实的短信验证码内容
 78         mobile = attrs['mobile']
 79         redis_conn = get_redis_connection('verify_codes')
 80         real_sms_code = redis_conn.get('sms_%s' % mobile) # bytes
 81 
 82         if not real_sms_code:
 83             raise serializers.ValidationError('短信验证码已过期')
 84 
 85         # 对比
 86         sms_code = attrs['sms_code'] # str
 87         real_sms_code = real_sms_code.decode() # str
 88         if sms_code != real_sms_code:
 89             raise serializers.ValidationError('短信验证码错误')
 90 
 91         return attrs
 92 
 93     def create(self, validated_data):
 94         """保存注册用户信息"""
 95         # 清除无用的数据
 96         del validated_data['password2']
 97         del validated_data['sms_code']
 98         del validated_data['allow']
 99 
100         # # 调用父类create方法
101         # user = super().create(validated_data)
102         #
103         # # 对密码进行加密
104         # password = validated_data['password']
105         # user.set_password(password)
106         # user.save()
107 
108         # 调用create_user方法
109         user = User.objects.create_user(**validated_data)
110 
111         # 注册成功就让用户处于登录状态
112         # 由服务器签发一个jwt token,保存用户身份信息
113         from rest_framework_jwt.settings import api_settings
114 
115         jwt_payload_handler = api_settings.JWT_PAYLOAD_HANDLER
116         jwt_encode_handler = api_settings.JWT_ENCODE_HANDLER
117 
118         # 生成载荷信息(payload)
119         payload = jwt_payload_handler(user)
120         # 生成jwt token
121         token = jwt_encode_handler(payload)
122 
123         # 给user对象增加一个属性token,保存jwt token信息
124         user.token = token
125 
126         # 返回user
127         return user
对应视图函数:
 1 from django.shortcuts import render
 2 from rest_framework import status
 3 from rest_framework.response import Response
 4 from rest_framework.views import APIView
 5 from rest_framework.generics import GenericAPIView, CreateAPIView
 6 
 7 from users.models import User
 8 from users.serializers import CreateUserSerializer
 9 # Create your views here.
10 
11 
12 # POST /users/
13 # class UserView(GenericAPIView):
14 class UserView(CreateAPIView):
15     serializer_class = CreateUserSerializer
16 
17     # def post(self, request):
18     #     """
19     #     注册用户信息的保存:
20     #     1. 获取参数并进行校验(参数完整性,两次密码是否一致,手机号格式,手机号是否已注册,短信验证码是否正确,是否同意协议)
21     #     2. 保存注册用户信息
22     #     3. 返回应答,注册成功
23     #     """
24     #     # 1. 获取参数并进行校验(参数完整性,两次密码是否一致,手机号格式,手机号是否已注册,短信验证码是否正确,是否同意协议)
25     #     serializer = self.get_serializer(data=request.data)
26     #     serializer.is_valid(raise_exception=True)
27     #
28     #     # 2. 保存注册用户信息(create)
29     #     serializer.save()
30     #
31     #     # 3. 返回应答,注册成功
32     #     return Response(serializer.data, status=status.HTTP_201_CREATED)
33 
34 
35 # url(r'^usernames/(?P<username>\w{5,20})/count/$', views.UsernameCountView.as_view()),
36 class UsernameCountView(APIView):
37     """
38     用户名数量
39     """
40     def get(self, request, username):
41         """
42         获取指定用户名数量
43         """
44         count = User.objects.filter(username=username).count()
45 
46         data = {
47             'username': username,
48             'count': count
49         }
50 
51         return Response(data)
52 
53 
54 # url(r'^mobiles/(?P<mobile>1[3-9]\d{9})/count/$', views.MobileCountView.as_view()),
55 class MobileCountView(APIView):
56     """
57     手机号数量
58     """
59     def get(self, request, mobile):
60         """
61         获取指定手机号数量
62         """
63         count = User.objects.filter(mobile=mobile).count()
64 
65         data = {
66             'mobile': mobile,
67             'count': count
68         }
69 
70         return Response(data)

用到的知识点:

##### 2. JWT token

1)session认证

```python
1. 接收用户名和密码
2. 判断用户名和密码是否正确
3. 保存用户的登录状态(session)
    session['user_id'] = 1
    session['username'] = 'smart'
    session['mobile'] = '13155667788'
4. 返回应答,登录成功
```

session认证的一些问题:

> 1. session存储在服务器端,如果登录的用户过多,服务器开销比较大。
> 2. session依赖于cookie,session的标识存在cookie中,可能会有CSRF(跨站请求伪造)。                      

2)jwt 认证机制(替代session认证)

```
1. 接收用户名和密码
2. 判断用户名和密码是否正确
3. 生成(签发)一个jwt token(token中保存用户的身份信息) 公安局(服务器)=>签发身份证(jwt token)
4. 返回应答,将jwt token信息返回给客户端。
```

如果之后需要进行身份认证,客户端需要将jwt token发送给服务器,由服务器验证jwt token的有效性。

3)jwt 的数据格式

```
eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiIxMjM0NTY3ODkwIiwibmFtZSI6Ik
pvaG4gRG9lIiwiYWRtaW4iOnRydWV9.TJVA95OrM7E2cBab30RMHrHDcEfxjoYZgeFO
```

a)headers头部

```
{
    "token类型声明",
    "加密算法"
}
```

base64加密(很容易被解密)

b)payload(载荷):用来保存有效信息

```
{
    "user_id": 1,
    "username": "smart",
    "mobile": "13155667788",
    "exp": "有效时间"
}
```

base64加密

c)signature(签名):防止jwt token被伪造

将headers和payload进行拼接,用.隔开,使用一个密钥(secret key)进行加密,加密之后的内容就是签名。

jwt token是由服务器生成,密钥保存在服务器端。

##### 3. jwt 扩展签发jwt token
from rest_framework_jwt.settings import api_settings

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)