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 ...
                    
                
                
            
        
浙公网安备 33010602011771号