from rest_framework.views import APIView from mysystem.models import Users from utils.jsonResponse import SuccessResponse,ErrorResponse,DetailResponse from rest_framework_simplejwt.serializers import TokenObtainPairSerializer from rest_framework_simplejwt.views import TokenObtainPairView from django.utils.translation import gettext_lazy as _ from rest_framework import serializers from utils.common import REGEX_MOBILE,get_parameter_dic,ast_convert import re from random import choice from django.db.models import Q,F from django_redis import get_redis_connection from utils.yunpian import YunPian from utils.uniappsms import UniCloudSms from django.contrib.auth.hashers import make_password, check_password import uuid from config import ALIYUN_SMS_SIGN,ALIYUM_SMS_TEMPLATE from utils.aliyunsms import send_sms from utils.tencentsms import tencentsms import json # ================================================= # # ************** 前端用户登录 view ************** # # ================================================= # """ 手机号密码登录 """ class AppMoliePasswordLoginSerializer(TokenObtainPairSerializer): """ 前端登录的序列化器: 重写djangorestframework-simplejwt的序列化器 """ default_error_messages = { 'no_active_account': _('该账号已被禁用,请联系管理员') } def validate(self, attrs): mobile = attrs['username'] # 验证手机号是否合法 if not re.match(REGEX_MOBILE, mobile): result = { "code": 4000, "msg": "请输入正确的手机号", "data": None } return result password = attrs['password'] user = Users.objects.filter(username=mobile, identity=2).first() if user and not user.is_active: result = { "code": 4000, "msg": "该账号已被禁用,请联系管理员", "data": None } return result if user and user.check_password(password): # check_password() 对明文进行加密,并验证 # data = super().validate(attrs) data={} refresh = self.get_token(user) data['identity'] = ast_convert(user.identity) data['userId'] = user.id data['refresh'] = str(refresh) data['access'] = str(refresh.access_token) result = { "code": 2000, "msg": "登录成功", "data": { "page": 1, "limit": 1, "total": 1, "data": data }, } else: result = { "code": 4000, "msg": "账号/密码不正确", "data": None } return result class APPMobilePasswordLoginView(TokenObtainPairView): """ 手机号密码登录接口(此种方式传参需要formdata方式) """ serializer_class = AppMoliePasswordLoginSerializer permission_classes = [] class UsernamePassWordLoginView(APIView): """ post: 账号密码登录 【功能描述】账号密码登录
【参数说明】username 手机号
【参数说明】password 密码
""" authentication_classes = [] permission_classes = [] def post(self,request): username = get_parameter_dic(request)['username'] password = get_parameter_dic(request)['password'] user = Users.objects.filter(username=username, identity=2).first() if user and not user.is_active: return ErrorResponse(msg="该账号已被禁用,请联系管理员") if user and user.check_password(password): # check_password() 对明文进行加密,并验证 resdata = APPMobileSMSLoginSerializer.get_token(user) return DetailResponse(data=resdata, msg="登录成功") return ErrorResponse(msg="账号/密码错误") class MobilePassWordLoginView(APIView): """ post: 手机号密码登录 【功能描述】手机号密码登录
【参数说明】mobile 手机号
【参数说明】password 密码
""" authentication_classes = [] permission_classes = [] def post(self,request): username = get_parameter_dic(request)['mobile'] password = get_parameter_dic(request)['password'] # 验证手机号是否合法 if not re.match(REGEX_MOBILE, username): return ErrorResponse(msg="请输入正确的手机号") user = Users.objects.filter(username=username, identity=2).first() if user and not user.is_active: return ErrorResponse(msg="该账号已被禁用,请联系管理员") if user and user.check_password(password): # check_password() 对明文进行加密,并验证 resdata = APPMobileSMSLoginSerializer.get_token(user) return DetailResponse(data=resdata, msg="登录成功") return ErrorResponse(msg="账号/密码错误") #发送短信序列化器 class SmsSerializer(serializers.Serializer): mobile = serializers.CharField(max_length=11,required=True,help_text="手机号") smstype=serializers.CharField(max_length=10,required=True,help_text="请求类型:login/register/restpass/rebind") # 函数名必须:validate + 验证字段名 def validate_mobile(self, mobile): """ 手机号码验证 """ # 是否合法手机号 if not re.match(REGEX_MOBILE, mobile): raise ValueError("手机号码非法") if self.context['smstype']== "register":#用户注册 # 是否已经注册 if Users.objects.filter(mobile=mobile).count(): raise ValueError("该手机号已注册") if self.context['smstype']== "restpass" or self.context['smstype']== "login":#重置密码/用户登录 #该手机号是否已注册 if not Users.objects.filter(username=mobile,identity=2,is_active=True).count(): raise ValueError("没有找到该用户") if self.context['smstype']== "wxbind":#微信绑定 #该手机号是否已注册 if not Users.objects.filter(username=mobile,identity=2,is_active=True,oauthwxuser__isnull=True).count(): raise ValueError("没有找到该用户或该用户已绑定微信") if self.context['smstype'] == "rebind":#换绑手机号,前提用户已经登录 #是否跟原来绑定过的号码一致 mqueryset = Users.objects.filter(id = self.context['request'].user.id) if not mqueryset: raise ValueError("该用户不存在") if mqueryset[0].mobile == mobile: raise ValueError("请使用新的手机号绑定") if Users.objects.filter(mobile=mobile).count(): raise ValueError("该手机号已被其他用户绑定") return mobile #发送短信验证码(不需要登录验证就可以调用) #注册,登录,忘记密码时使用 class SendSmsCodeView(APIView): """ post: 发送手机验证码 【参数说明】使用"application/json"编码传输,参数如下
mobile 必要 手机号
smstype 必要 短信类型:register/restpass/login
""" authentication_classes = ()#不需要身份认证 permission_classes = ()#不需要权限分配 serializer_class = SmsSerializer def generate_code(self): """ 生成四位数字的验证码 """ seeds = "1234567890" random_str = [] for i in range(6): random_str.append(choice(seeds)) return "".join(random_str) def post(self, request, *args, **kwargs): mobile = get_parameter_dic(request)['mobile'] smstype = get_parameter_dic(request)['smstype'] if smstype == "login" or smstype == "restpass" or smstype == "wxbind" or smstype == "register": # 创建序列化器 serializer = SmsSerializer(data=request.data, context={"request": request, "smstype": smstype}) # 验证是否有效 serializer.is_valid(raise_exception=True) # 判断该手机号60s内是否已经发送过短信 redis_conn = get_redis_connection('verify_codes') send_flag = redis_conn.get('send_flag_%s' % mobile) if send_flag: # 如果取到了标记,说明该手机号60s内发送过短信验证码 return ErrorResponse(msg="请一分钟后再获取验证码") # 验证码过期时间 codeexpire = 300 # 300秒,默认5分钟 # 生成验证码 code = self.generate_code() # 云片网api短信接口调用-----------开始 # yun_pian = YunPian(SMS_API_KEY) # sms_status = yun_pian.send_sms(code=code, mobile=mobile) # # if sms_status["code"] != 0: # mydata = {} # mydata["mobile"] = mobile # return Response(ly_api_res(400,mydata,sms_status["msg"]), status=status.HTTP_400_BAD_REQUEST) # else: # #存储短信验证码到redis # redis_conn.setex('sms_%s'%mobile,codeexpire,code)#默认300秒5分钟过期时间 # #存储一个标记,表示此手机号已发送过短信,标记有效期为60s # redis_conn.setex('send_flag_%s'%mobile,60,1) # mydata = {} # mydata["mobile"] = mobile # return Response(ly_api_res(200,mydata,"短信验证码发送成功"), status=status.HTTP_200_OK) # 云片网api短信接口调用-----------结束 # unicloud短信接口api调用-----------开始 # unicloudsms = UniCloudSms() # sms_status = unicloudsms.send_sms(code=code, mobile=mobile,expminute=codeexpire) # #返回内容 # #{"msg":"sendSms参数phone值不可为空"} # #{"code":0,"errCode":0,"success":true} # if 'code' in sms_status: #判断返回内容是否存在code的key,错误时不返回code # if sms_status["code"] == 0: # # 存储短信验证码到redis # redis_conn.setex('sms_%s' % mobile, codeexpire, code) # 默认300秒5分钟过期时间 # # 存储一个标记,表示此手机号已发送过短信,标记有效期为60s # redis_conn.setex('send_flag_%s' % mobile, 60, 1) # mydata = {} # mydata["mobile"] = mobile # return SuccessResponse(data=mydata, msg="短信验证码发送成功") # else: # mydata = {} # mydata["mobile"] = mobile # return ErrorResponse(data=mydata, msg=sms_status['msg']) # else: # mydata = {} # mydata["mobile"] = mobile # return ErrorResponse(data=mydata, msg=sms_status['msg']) # unicloud短信接口api调用-----------结束 # 阿里云短信-----------开始 # __business_id = uuid.uuid1() # # 一个验证码发送的例子 # params = '{\"code\":\"'+code+'\"}' # 模板参数 # sms_status= send_sms(__business_id,mobile,ALIYUN_SMS_SIGN,ALIYUM_SMS_TEMPLATE,params) # # 返回内容byte类型 # # { # # "Message": "OK", # # "RequestId": "F655A8D5-B967-440B-8683-DAD6FF8DE990", # # "Code": "OK", # # "BizId": "900619746936498440^0" # # } # sms_status_str = sms_status.decode() # sms_status_json = json.loads(sms_status) # if 'Code' in sms_status_str: # 判断返回内容是否存在code的key,错误时不返回code # if sms_status_json["Code"] == 'OK': # # 存储短信验证码到redis # redis_conn.setex('sms_%s' % mobile, codeexpire, code) # 默认300秒5分钟过期时间 # # 存储一个标记,表示此手机号已发送过短信,标记有效期为60s # redis_conn.setex('send_flag_%s' % mobile, 60, 1) # mydata = {} # mydata["mobile"] = mobile # return SuccessResponse(data=mydata, msg="短信验证码发送成功") # else: # return ErrorResponse(data=sms_status_json, msg='发送失败') # else: # return ErrorResponse(data=sms_status_json, msg='发送失败') # 阿里云短信-----------结束 # 腾讯云短信-----------开始 sms_status_json = tencentsms("+" + str(86) + str(mobile), code) # sms_status_json = 'Ok' if 'Ok' in sms_status_json: # # 存储短信验证码到redis redis_conn.setex('sms_%s' % mobile, codeexpire, code) # 默认300秒5分钟过期时间 # 存储一个标记,表示此手机号已发送过短信,标记有效期为60s redis_conn.setex('send_flag_%s' % mobile, 60, 1) mydata = {} mydata["mobile"] = mobile return SuccessResponse(data=mydata, msg="success") else: return ErrorResponse(data=sms_status_json, msg='error') # 腾讯云短信-----------结束 else: return ErrorResponse(msg="smstype短信验证码类型错误") class APPMobileSMSLoginSerializer(TokenObtainPairSerializer): """ 登录的序列化器: 重写djangorestframework-simplejwt的序列化器 """ @classmethod def get_token(cls, user): refresh = super(APPMobileSMSLoginSerializer,cls).get_token(user) data = {} data['identity'] = ast_convert(user.identity) data['userId'] = user.id data['refresh'] = str(refresh) data['access'] = str(refresh.access_token) return data class APPMobileSMSLoginView(APIView): """ post: 手机号短信登录接口 【功能描述】用户手机号短信验证码登录
【参数说明】mobile为手机号
【参数说明】code短信验证码
""" authentication_classes = [] permission_classes = [] def post(self,request): mobile = get_parameter_dic(request)['mobile'] code = get_parameter_dic(request)['code'] # 验证手机号是否合法 if not re.match(REGEX_MOBILE, mobile): return ErrorResponse(msg="请输入正确手机号") # 判断短信验证码是否正确 redis_conn = get_redis_connection('verify_codes') send_flag = redis_conn.get('sms_%s' % mobile) # send_flag的值为bytes,需要转换成str ,send_flag.decode() if not send_flag: # 如果取不到标记,则说明验证码过期 return ErrorResponse(msg="短信验证码已过期") else: if str(send_flag.decode()) != str(code): return ErrorResponse(msg="验证码错误") #开始登录 user = Users.objects.filter(username=mobile,identity=2).first() if not user: return ErrorResponse(msg="用户不存在") if not user.is_active: return ErrorResponse(msg="该账号已被禁用,请联系管理员") resdata = APPMobileSMSLoginSerializer.get_token(user) redis_conn.delete('sms_%s' % mobile) return SuccessResponse(data=resdata, msg="登录成功") #用户忘记密码重置密码 class ForgetPasswdResetView(APIView): ''' post: 【功能描述】重置用户密码
【参数说明】mobile为手机号
【参数说明】code短信验证码
【参数说明】password为密码
''' authentication_classes = [] permission_classes = [] def post(self, request, *args, **kwargs): mobile = get_parameter_dic(request)['mobile'] code = get_parameter_dic(request)['code'] password = get_parameter_dic(request)['password'] if len(password) < 6: return ErrorResponse(msg="密码长度至少6位") # 验证手机号是否合法 if not re.match(REGEX_MOBILE, mobile): return ErrorResponse(msg="请输入正确手机号") # 判断短信验证码是否正确 redis_conn = get_redis_connection('verify_codes') send_flag = redis_conn.get('sms_%s'%mobile)#send_flag的值为bytes,需要转换成str ,,send_flag.decode() if not send_flag: # 如果取不到标记,则说明验证码过期 return ErrorResponse(msg="短信验证码已过期") else: if str(send_flag.decode()) != str(code): return ErrorResponse(msg="验证码错误") #开始更换密码 user = Users.objects.filter(username=mobile,identity=2).first() if not user: return ErrorResponse(msg="用户不存在") if not user.is_active: return ErrorResponse(msg="该账号已被禁用,请联系管理员") # 重置密码 user.password = make_password(password) user.save() redis_conn.delete('sms_%s' % mobile) return SuccessResponse(msg="success") class RegisterView(APIView): ''' 前端用户注册 post: 【功能描述】前端用户注册
【参数说明】mobile为手机号
【参数说明】code短信验证码
【参数说明】password为密码
【参数说明】password2为确认输入的密码
''' authentication_classes = [] permission_classes = [] def post(self, request, *args, **kwargs): mobile = get_parameter_dic(request)['mobile'] code = get_parameter_dic(request)['code'] password = get_parameter_dic(request)['password'] password2 = get_parameter_dic(request)['password2'] if mobile is None or code is None or password is None or password2 is None: return ErrorResponse(msg="提交的参数不能为空") # 判断密码是否合法 if len(password) < 6: return ErrorResponse(msg="密码长度至少6位") if not re.match(r'^[a-zA_Z0-9]{6,20}$', password): return ErrorResponse(msg="密码格式不正确(大小写字母、数字组合)") if password != password2: return ErrorResponse(msg="两次密码输入不一致") # 验证手机号是否合法 if not re.match(REGEX_MOBILE, mobile): return ErrorResponse(msg="请输入正确手机号") # 判断短信验证码是否正确 if not re.match(r'^\d{6}$', code): return ErrorResponse(msg="验证码格式错误") redis_conn = get_redis_connection('verify_codes') send_flag = redis_conn.get('sms_%s' % mobile) # send_flag的值为bytes,需要转换成str ,,send_flag.decode() if not send_flag: # 如果取不到标记,则说明验证码过期 return ErrorResponse(msg="短信验证码已过期") else: if str(send_flag.decode()) != str(code): return ErrorResponse(msg="验证码错误") # 开始注册 Users.objects.create(username=mobile,password=make_password(password),is_staff=False,identity=2) redis_conn.delete('sms_%s' % mobile) return SuccessResponse(msg="注册成功")