博客
关于我
强烈建议你试试无所不能的chatGPT,快点击我
drf实现图片验证码功能
阅读量:5840 次
发布时间:2019-06-18

本文共 9689 字,大约阅读时间需要 32 分钟。

一.背景

  在之前实现过django的图片验证码,有自己实现过的,也有基于django-simple-captcha的,都是基于form表单验证,若自己实现,可以获取相应的标签name便可以获取判断,若使用django-simple-captcha只需相应配置即可。但在前后端分离的情况下,就有点摸不着头脑了,序列化时CaptchaField不管作用,百度搜索也没找到相应的办法,于是心想只有重写restframework_jwt自带的登录验证接口及注册接口,都得新增字段。

二.django-simple-captcha简单介绍

  Django Simple Captcha是一个非常简单但高度可定制的Django应用程序,可以将captcha图像添加到任何Django表单中。

  网址如下:,只需按照文档简单配置即可,该插件会对应生成一张表,存放验证码信息及过期时间等。

三.基于django-simple-captcha的drf图片验证码

  1.生成图片验证码接口(我这里将图片转换成了base64),也可以是图片或地址:

from django.http import HttpResponsefrom captcha.views import CaptchaStore, captcha_imageimport base64.......class ImageView(APIView):    def get(self, request):        hashkey = CaptchaStore.generate_key()        try:        #获取图片id            id_ = CaptchaStore.objects.filter(hashkey=hashkey).first().id            imgage = captcha_image(request, hashkey)        #将图片转换为base64            image_base = base64.b64encode(imgage.content)            json_data = json.dumps({
"id": id_, "image_base": image_base.decode('utf-8')}) except: json_data = None return HttpResponse(json_data, content_type="application/json")

 

  2.注册时的图片验证码:

    2.1序列化:

    这里把图片验证码的id和用户输入的内容都传过来,不易错误,captcha生成的表中的字段expiration是生成图片的时间加五分钟,因此captcha判断的也就是这个过期时间,重写也可以判断这个时间,若当前时间大于它则过期(有效时间五分钟)。

class UserRegSerializer(serializers.ModelSerializer):    """"    用户注册序列化    """    username = serializers.CharField(min_length=2, max_length=20,                                     error_messages={                                         "max_length": "用户名长度应小于等于20",                                         "min_length": "用户名长度应大于等于2"},                                     validators=[UniqueValidator(queryset=User.objects.all(), message='用户名已经被使用')],help_text="用户名")    code = serializers.CharField(required=True, write_only=True, max_length=4, min_length=4, label='验证码',                                 error_messages={                                     "blank": "请输入邮箱验证码",                                     "required": "邮箱验证码不能为空",                                     "max_length": "邮箱验证码格式错误",                                     "min_length": "邮箱验证码格式错误"                                 }, help_text='邮箱验证码')    email = serializers.CharField(required=True, allow_blank=False,                                  validators=[UniqueValidator(queryset=User.objects.all(), message='用户已经存在')],help_text="邮箱")    password = serializers.CharField(style={
"input_type": "password"}, write_only=True,help_text="密码") captcha = serializers.CharField(min_length=4, max_length=4, required=True, error_messages={ "max_length": "图片验证码格式错误", "min_length": "图片验证码格式错误", "required": "请输入图片验证码" },help_text="图片验证吗") ima_id = serializers.CharField(required=True, write_only=True, allow_blank=False,help_text="图片验证码id") def validate_code(self, code): verify_codes = EmailVeriyRecord.objects.filter(email=self.initial_data['email']).order_by('-send_time') if verify_codes: last_verfycode = verify_codes[0] five_minute_ago = datetime.now() - timedelta(hours=0, minutes=5, seconds=0) if five_minute_ago > last_verfycode.send_time: raise serializers.ValidationError('验证码过期') if code != last_verfycode.code: raise serializers.ValidationError('验证码错误') else: raise serializers.ValidationError('验证码错误') def validate_password(self, password): """ 密码长度大于6小于12 """ if re.match(REGEX_PWD, password): pass else: raise serializers.ValidationError("密码必须包含数字,字母,特殊符中两到三种,且长度在6-12之间") return password def validate_captcha(self, captcha): try: captcha = captcha.lower() except: raise serializers.ValidationError("图片验证码错误") image_code = CaptchaStore.objects.filter( id=self.initial_data['ima_id']).first() if image_code and datetime.now() > image_code.expiration: raise serializers.ValidationError('图片验证码过期') else: if image_code and image_code.response == captcha: pass else: raise serializers.ValidationError("图片验证码错误") # 作用于所有字段 def validate(self, attrs): if attrs['username']: pass else: attrs['username'] = attrs['email'] del attrs["code"] del attrs["ima_id"] del attrs["captcha"] return attrs class Meta: model = User fields = ('email', 'code', 'password', 'username', 'captcha', 'ima_id')

 

     2.2注册view实现:

class UserRegisterViewset(mixins.CreateModelMixin, mixins.UpdateModelMixin,                          mixins.RetrieveModelMixin, viewsets.GenericViewSet):    """       用户注册接口    create:        用户添加    """    serializer_class = UserRegSerializer    queryset = User.objects.all()    authentication_classes = (JSONWebTokenAuthentication, authentication.SessionAuthentication)    def create(self, request, *args, **kwargs):        serializer = self.get_serializer(data=request.data)        serializer.is_valid(raise_exception=True)        user = self.perform_create(serializer)        re_dict = serializer.data        payload = jwt_payload_handler(user)        re_dict['token'] = jwt_encode_handler(payload)        headers = self.get_success_headers(serializer.data)        return Response(re_dict, status=status.HTTP_201_CREATED, headers=headers)    def get_serializer_class(self):        '''        重载GenericAPIView中的get_serializer_class函数,调用不同的序列化类,如果是create,        就调用UserRegSerializer序列化,否则UserDetailSerializer序列化        :return:         '''        if self.action == 'retrieve':            return UserDetailSerializer        elif self.action == 'create':            return UserRegSerializer        return UserDetailSerializer    def get_permissions(self):        '''        重载APIview中的get_perimissions函数,如果是新增用户则不用登录,否则必须登录        :return:         '''        if self.action == 'retrieve':            return [permissions.IsAuthenticated()]        elif self.action == 'create':            return []        return []    def get_object(self):        '''        返回当前用户        :return:         '''        return self.request.user    def perform_create(self, serializer):        return serializer.save()

 

   2.登录:

    2.1序列化:

from rest_framework_jwt.settings import api_settingsjwt_payload_handler = api_settings.JWT_PAYLOAD_HANDLERjwt_encode_handler = api_settings.JWT_ENCODE_HANDLERfrom django.contrib.auth import authenticatefrom rest_framework_jwt.compat import PasswordFieldclass MyloginSerializer(JSONWebTokenSerializer):    """    从写登录序列化    """    def __init__(self, *args, **kwargs):        """        Dynamically add the USERNAME_FIELD to self.fields.        """        super(JSONWebTokenSerializer, self).__init__(*args, **kwargs)        self.fields[self.username_field] = serializers.CharField()        self.fields['password'] = PasswordField(write_only=True)        self.fields['captcha'] = serializers.CharField(min_length=4, max_length=4, required=True,                                                       error_messages={                                                           "max_length": "图片验证码格式错误",                                                           "min_length": "图片验证码格式错误",                                                           "required": "请输入图片验证码"                                                       })        self.fields['ima_id'] = serializers.CharField(required=True, allow_blank=False)    def validate_captcha(self, captcha):        image_code = CaptchaStore.objects.filter(            id=self.initial_data['ima_id']).first()        five_minute_ago = datetime.now() - timedelta(hours=0, minutes=5, seconds=0)        if image_code and five_minute_ago > image_code.expiration:            raise serializers.ValidationError('验证码过期')        else:            if image_code and (image_code.response == captcha or image_code.challenge == captcha):                pass            else:                raise serializers.ValidationError("图片验证码错误")    def validate(self, attrs):        del attrs["ima_id"]        del attrs["captcha"]        credentials = {            self.username_field: attrs.get(self.username_field),            'password': attrs.get('password')        }        if all(credentials.values()):            user = authenticate(**credentials)            if user:                if not user.is_active:                    msg = _('User account is disabled.')                    raise serializers.ValidationError(msg)                payload = jwt_payload_handler(user)                return {                    'token': jwt_encode_handler(payload),                    'user': user                }            else:                msg = _('Unable to log in with provided credentials.')                raise serializers.ValidationError(msg)        else:            msg = _('Must include "{username_field}" and "password".')            msg = msg.format(username_field=self.username_field)            raise serializers.ValidationError(msg)

    2.2view:

from rest_framework_jwt.views import JSONWebTokenAPIViewfrom .serializers import MyloginSerializerclass MyJSONWebToken(JSONWebTokenAPIView):    """"    重写jwt的登录验证,含图片验证码    """    serializer_class = MyloginSerializer

  3.url中相应配置:

url(r'images/$',ImageView.as_view()),    url(r'login/$',MyJSONWebToken.as_view(),name="login")

 

四.总结

  这样做功能是实现了,但感觉有些粗糙,可以把验证码表的数据超过某个时间的自动清除,也可以把数据放入缓存(redis等),希望能提一些改进的建议。

 

 

 

 

转载于:https://www.cnblogs.com/lyq-biu/p/10077820.html

你可能感兴趣的文章
vim
查看>>
bzoj2095: [Poi2010]Bridges(二分+混合图求欧拉回路)
查看>>
cmake 变量
查看>>
[Programming Entity Framework] 第2章 探究实体数据模型(EDM)(一)
查看>>
shell环境
查看>>
Java调用C++类库--JNI
查看>>
gles和opengl版本对照表
查看>>
微信开发(二)自己的代码
查看>>
python netwokx环境搭建
查看>>
面向空实现类继承
查看>>
1303: Decimal
查看>>
奥数 --- 找规律 + 总结
查看>>
数据类型和变量
查看>>
多线程糗事百科案例(python2)
查看>>
Jsp验证码
查看>>
[JavaScript] JavaScript作用域深度解析
查看>>
boost Asio网络编程简介
查看>>
node-webkit-updater——NW.js自动更新
查看>>
maven 加入json-lib.jar 报错 Missing artifact net.sf.json-lib:json-lib:jar:2.4:compile
查看>>
Java多线程入门Ⅱ
查看>>