4.登录、验证、注释、token

1.drf的token登录和原理

 1 # settings.py
 2 INSTALLED_APPS = [
 3    ...
 4     'rest_framework.authtoken',
 5 ]
 6 # urls.py
 7 ...
 8 from rest_framework.authtoken import views
 9 ...
10 urlpatterns = [
11    ...
12     url(r'^api-token-auth/', views.obtain_auth_token),
13    ...
14 ]+ static(settings.MEDIA_URL, document_root=settings.MEDIA_ROOT)

    读取与创建:

1 # POST请求
2  curl -d 'username=admin2&password=admin2123456' 'http://192.168.2.129:8088/api-token-auth/'
3 # 返回结果:
4 {"token":"60d25705a9a0fad398daa0e43bb556ed36e4723e"}
5 
6 # 加上认证进行请求
7 curl -X GET http://192.168.2.129:8088/goods/ -H 'Authorization: Token 60d25705a9a0fad398daa0e43bb556ed36e4723e'

 2.viewsets配置认证类

 1 # 之前是通过全局配置token,这时我们请求公共数据时如果token失败就会造成请求失败,这是我们不希望看到的。所以我们可以在我们需要token认证的类中添加token认证机制
 2 
 3 # settings.py配置全局token认证
 4 REST_FRAMEWORK = {
 5     'DEFAULT_AUTHENTICATION_CLASSES': (
 6         'rest_framework.authentication.BasicAuthentication',
 7         'rest_framework.authentication.SessionAuthentication',
 8         # 'rest_framework.authentication.TokenAuthentication',
 9     )
10 }
11 
12 # 局部token认证,在需要认证的类中,添加:
13 ...
14 from rest_framework.authentication import TokenAuthentication
15 ...
16 authentication_classes = (TokenAuthentication, )
17 ...

 3.json web token的原理

    前后端分离之JWT用户认证( http://lion1ou.win/2017/01/18/ )

    可以用它来实现单点登录、多域名验证等,JWT生成的token是分散在客户端上的,缓解服务器的存储与数据库读取压力,服务器只需要进行加密与解密的工作。

4. json web token方式完成用户认证

    drf jwt 使用说明文档:http://getblimp.github.io/django-rest-framework-jwt/

 1 # 安装
 2 pip install djangorestframework-jwt
 3 
 4 # settings.py
 5 REST_FRAMEWORK = {
 6     'DEFAULT_AUTHENTICATION_CLASSES': (
 7       ...
 8         'rest_framework_jwt.authentication.JSONWebTokenAuthentication',
 9     )
10 }
11 
12 # urls.py
13 from rest_framework_jwt.views import obtain_jwt_token
14 #...
15 
16 urlpatterns = [
17     '',
18     # ...
19 
20     url(r'^api-token-auth/', obtain_jwt_token),
21 ]
22 
23 # 测试
24 # 创建token
25 #   curl -X POST -H "Content-Type: application/json" -d '{"username":"admin","password":"password123"}' http://localhost:8000/api-token-auth/
26 # 添加token
27 #    curl -H "Authorization: JWT <your_token>" http://localhost:8000/protected-url/

 5.vue和jwt接口调试

 1 # urls.py
 2 url(r'^api_jwt_auth/', obtain_jwt_token),
 3 # 修改为
 4 url(r'^login/', obtain_jwt_token),
 5 
 6 # settings.py,增加自定义登录后端
 7 AUTHENTICATION_BACKENDS = (
 8     'users.views.CustomerBackend',
 9 )
10 
11 # users/views.py
12 from django.contrib.auth.backends import ModelBackend
13 from django.contrib.auth import get_user_model
14 from django.db.models import Q
15 
16 User = get_user_model()
17 
18 class CustomerBackend(ModelBackend):
19     """
20     自定义用户登录
21     """
22 
23     def authenticate(self, request, username=None, password=None, **kwargs):
24         try:
25             user = User.objects.get(Q(username=username)|Q(mobile=username))
26             if user.check_password(password):
27                 return user
28         except Exception, e:
29             return None

 6.云片网发送短信验证码

云片网设置流程:

    新增签名==》新建模版(模版类型:验证码;模板内容:【慕学新鲜】你的验证码是#code#,如非本人操作,请忽略本短信)==》
API文档==》使用说明==》国内短信API==》单条发送==》

 1 # utils/yunpian.py
 2 import json
 3 import requests
 4 
 5 
 6 class YunPian(object):
 7 
 8     def __init__(self, api_key):
 9         self.api_key = api_key
10         self.single_send_url = "https://sms.yunpian.com/v2/sms/single_send.json"
11 
12     def send_sms(self, code, mobile):
13         parmas = {
14             "apikey": self.api_key,
15             "mobile": mobile,
16             "text": "【****】您的验证码是{code}。如非本人操作,请忽略本短信".format(code=code)
17         }
18 
19         response = requests.post(self.single_send_url, data=parmas)
20         re_dict = json.loads(response.text)
21         return re_dict
22 
23 
24 if __name__ == "__main__":
25     yun_pian = YunPian("APIKEY")
26     res = yun_pian.send_sms("要发送的验证码", "手机号")
27     print (res)
28 
29 # 注意:【****】必须与云片网上的签名一致;APIKEY:对应云片网上的APIKEY;

 7.drf实现发送短信验证码接口

1 # settings.py
2 ...
3 # 手机验证规则
4 REGEX_MOBILE = "^1[358]\d{9}$|^147\d{8}$|^176\d{8}$"
5 
6 # 云片网APIKEY
7 APIKEY = "...."
8 ...
1 # urls.py
2 ...
3 # 配置发送验证码路由
4 router.register(r'codes', SmsCodeViewset, base_name='codes')
5 ...
 1 # serializers.py
 2 
 3 import re, datetime
 4 from django.contrib.auth import get_user_model
 5 from rest_framework import serializers
 6 
 7 from MxShop.settings import REGEX_MOBILE
 8 from .models import VerifyCode
 9 
10 User = get_user_model()
11 
12 class SmsSerializer(serializers.Serializer):
13     mobile = serializers.CharField(max_length=11, required=True)
14 
15     def validate_mobile(self, mobile):
16         '''
17         验证手机号码是否合法
18         :param mobile:
19         :return:
20         '''
21 
22         # 正则验证手机号格式是否正确
23         if not re.match(REGEX_MOBILE, mobile):
24             raise serializers.ValidationError('手机号码格式不正确')
25 
26         # 手机是否已经注册
27         if User.objects.filter(mobile=mobile).count():
28             raise serializers.ValidationError('手机号码已经被注册')
29 
30         # 发送频率
31         one_minutes_ago = datetime.datetime.now() - datetime.timedelta(hours=0, minutes=1, seconds=0)
32         if VerifyCode.objects.filter(add_time__gt=one_minutes_ago, mobile=mobile):
33             raise serializers.ValidationError('距离上一次发送未超过60秒')
34 
35         return mobile
 1 # views.py
 2 
 3 from rest_framework import viewsets, status
 4 from rest_framework.mixins import CreateModelMixin
 5 from rest_framework.response import Response
 6 
 7 from .serializers import SmsSerializer
 8 from utils.yunpian import YunPian
 9 from .models import VerifyCode
10 from MxShop.settings import APIKEY
11 
12 User = get_user_model()
13 
14 class SmsCodeViewset(CreateModelMixin, viewsets.GenericViewSet):
15     '''
16     发送短信验证码
17     '''
18     serializer_class = SmsSerializer
19 
20     def generate_code(self):
21         '''
22         生成四位数随机数
23         :return:
24         '''
25         seeds = '1234567890'
26         rand_arr = []
27         for i in range(4):
28             rand_arr.append(choice(seeds))
29         return ''.join(rand_arr)
30 
31     def create(self, request, *args, **kwargs):
32         serializer = self.get_serializer(data=request.data)
33         serializer.is_valid(raise_exception=True)
34 
35         # 获取验证后的手机号
36         mobile = serializer.validated_data['mobile']
37 
38         # 调用发送验证码
39         yunpian = YunPian(APIKEY)
40         # 获取四位数随机验证码
41         code = self.generate_code()
42         sms_status = yunpian.send_sms(code, mobile)
43 
44         if sms_status['code'] == 0:
45             # 写入数据库
46             verifyCode = VerifyCode(mobile=mobile, code=code)
47             verifyCode.save()
48             return Response({'mobile': mobile}, status=status.HTTP_201_CREATED)
49         else:
50             return Response({'mobile': sms_status['msg']}, status=status.HTTP_400_BAD_REQUEST)

 8.user serializer和validator验证

1 # urls.py 路由配置
2 # 配置用户登录注册路由
3 router.register(r'users', UserViewset, base_name='users')
1 # users/views.py
2 
3 class UserViewset(CreateModelMixin, viewsets.GenericViewSet):
4     serializer_class = UserRegSerializer
 1 # users/serializers.py
 2 
 3 import re, datetime
 4 from django.contrib.auth import get_user_model
 5 from rest_framework import serializers
 6 from rest_framework.validators import UniqueValidator
 7 
 8 from MxShop.settings import REGEX_MOBILE
 9 from .models import VerifyCode
10 
11 User = get_user_model()
12 
13 class UserRegSerializer(serializers.ModelSerializer):
14     # 添加Model里不存在的字段code
15     code = serializers.CharField(max_length=4, min_length=4, write_only=True, help_text='验证码', error_messages={
16         'blank': '验证码不能为空',
17         'required': '请输入验证码',
18         'max_length': '验证码格式不正确!',
19         'min_length': '验证码格式不正确!',
20     })
21     username = serializers.CharField(required=True, validators=[UniqueValidator(queryset=User.objects.all(),
22                                     message='用户名已存在!')])
23 
24     def validate(self, attrs):
25         attrs['mobile'] = attrs['username']
26         del attrs['code']
27         return attrs
28 
29     def validate_code(self, code):
30         # 用户前端传过来的值都会放到initial_data[]中
31         # 这里为什么不直接用get()?如果用get()就捕获当数据不存在时的异常。
32         verify_records = VerifyCode.objects.filter(mobile=self.initial_data['username']).order_by("-add_time")
33         if verify_records:
34             last_record = verify_records[0]
35 
36             # 验证码是否超个五分钟
37             five_minutes_ago = datetime.datetime.now() - datetime.timedelta(hours=0, minutes=5, seconds=0)
38             if last_record['add_time'] > five_minutes_ago:
39                 raise serializers.ValidationError('验证码超时')
40 
41             if last_record['code'] != code:
42                 raise serializers.ValidationError('验证码错误')
43 
44         else:
45             raise serializers.ValidationError('验证码错误')
46 
47     class Meta:
48         model = User
49         fields = ('username', 'code', 'mobile')

9. django信号量实现用户密码修改

1 # views.py
2 # 创建密码,并且不将密码返回给前端
3 ...
4 class UserRegSerializer(serializers.ModelSerializer):
5 ...
6   password = serializers.CharField(required=True, write_only=True, help_text='密码', label='密码', style={'input_type': 'password'})
7 ...

    解决密码是明文的问题:

 1 # 方法1
 2 # users/serializers.py
 3 ...
 4 class UserRegSerializer(serializers.ModelSerializer):
 5     ...
 6     def create(self, validated_data):
 7         user = super(UserRegSerializer, self).create(validated_data=validated_data)
 8         user.set_password(validated_data['password'])
 9         user.save()
10         return user
11 ...
 1 # 方法2  使用django signals(http://python.usyiyi.cn/translate/Django_111/ref/signals.html)
 2 
 3 # users/signals.py
 4 from django.db.models.signals import post_save
 5 from django.dispatch import receiver
 6 from django.contrib.auth import get_user_model
 7 
 8 User = get_user_model()
 9 
10 @receiver(post_save, sender=User)
11 def create_auth_token(sender, instance=None, created=False, **kwargs):
12     if created:
13         password = instance.password
14         instance.set_password(password)
15         instance.save()
16 
17 # users/apps.py
18 from django.apps import AppConfig
19 class UsersConfig(AppConfig):
20     name = 'users'
21     verbose_name = '用户管理'
22 
23     def ready(self):
24         import users.signals

****方法2:没有调试出来????

10.vue和注册功能联调

 1 # users/views.py
 2 # 生成JWT相应的token
 3 ...
 4 from rest_framework_jwt.serializers import jwt_payload_handler, jwt_encode_handler
 5 ...
 6 class UserViewset(CreateModelMixin, viewsets.GenericViewSet):
 7 ...
 8     def create(self, request, *args, **kwargs):
 9         serializer = self.get_serializer(data=request.data)
10         serializer.is_valid(raise_exception=True)
11 
12         user = self.perform_create(serializer)
13         re_dict = serializer.validated_data
14         payload = jwt_payload_handler(user)
15         re_dict['token'] = jwt_encode_handler(payload)
16 
17         headers = self.get_success_headers(serializer.data)
18         return Response(re_dict, status=status.HTTP_201_CREATED, headers=headers)
19 
20     def perform_create(self, serializer):
21         return serializer.save()
22 ...

 

posted @ 2018-01-24 17:26  KD-VS-WB  阅读(737)  评论(0)    收藏  举报