• pillow图形验证码


    图片验证码逻辑

    1. 客户端发起GET连接请求,并随机生成UUID,绑定图片
      • UUID:通用唯一识别码(Universally Unique Identifier),目的,是让分布式系统中的所有元素,都能有唯一的辨识信息,每个人都可以创建不与其它人冲突的UUID
    2. 服务端生成图片验证码,图片存入内存并返回到客户端
    3. 服务端存储源字符串到session中,也可以存入缓存中,例memcachedredis
    4. 客户端表单填写验证码原值
    5. 移出表单框时间触发异步post请求验证,访问时,图片uuid作为属性绑定到表单属性中,作为post提交的数据一部分
    6. 服务端验证时通过UUIDkey,表单值为value进行图片验证码校验

    图片验证码使用

      下载pillow

    pip install pillow

      在使用的时候需要设置pillow需要的字体。需要复制到django项目中

    设置字体文件的路径

    FONTS_DIRS =  os.path.join(BASE_DIR, 'fonts',)   #找到字体文件的路径
    setting.py

    生成图片

    from django.contrib import admin
    from django.urls import path
    from . import views
    urlpatterns = [
        path("generate_image_code/<str:generate_image_id>/",views.generate_image_code),
        
    
    ]
    urls.py
    from PIL import Image, ImageDraw, ImageFont
    from shiyanloupro.settings import *
    from django.http.response import HttpResponse
    
    def generate_image_code(request, generate_image_id):
        '''
            本地图片验证码生成函数
        '''
        bgcolor = (random.randrange(20, 100), random.randrange(
            20, 100), random.randrange(20, 100))
        width = 110
        height = 40
        # 创建画面对象
        im = Image.new('RGB', (width, height), bgcolor)
        # 创建画笔对象
        draw = ImageDraw.Draw(im)
        # 调用画笔的point()函数绘制噪点
        for i in range(0, 100):
            xy = (random.randrange(0, width), random.randrange(0, height))
            fill = (random.randrange(0, 255), 255, random.randrange(0, 255))
            draw.point(xy, fill=fill)
        # 定义验证码的备选值
        str = '1234567890QWERTYUIOPASDFGHJKLZXCVBNMqwertyuiopasdfghjklzxcvbnm'
        # 随机选取4个值作为验证码
        rand_str = ''
        for i in range(0, 4):
            rand_str += str[random.randrange(0, len(str))]
        # 构造字体对象
    
        fonts_files = os.path.join(
            FONTS_DIRS, 'SourceCodePro-Bold.ttf')
        font = ImageFont.truetype(fonts_files, 30)
        # 构造字体颜色
        fontcolor1 = (255, random.randrange(0, 255), random.randrange(0, 255))
        fontcolor2 = (255, random.randrange(0, 255), random.randrange(0, 255))
        fontcolor3 = (255, random.randrange(0, 255), random.randrange(0, 255))
        fontcolor4 = (255, random.randrange(0, 255), random.randrange(0, 255))
        # 绘制4个字
        draw.text((5, 2), rand_str[0], font=font, fill=fontcolor1)
        draw.text((25, 2), rand_str[1], font=font, fill=fontcolor2)
        draw.text((50, 2), rand_str[2], font=font, fill=fontcolor3)
        draw.text((75, 2), rand_str[3], font=font, fill=fontcolor4)
        # 释放画笔
        del draw
        # 存入缓存,用于做进一步验证,并设置超时时间为10分组
        cache.set(generate_image_id,rand_str,60*10)
        buf = io.BytesIO()
        # 将图片保存在内存中,文件类型为png
        im.save(buf, 'png')
        # 将内存中的图片数据返回给客户端,MIME类型为图片png!
        return HttpResponse(buf.getvalue(), 'image/png')
    views.py

    vue生成uuid

    generate_uuid: function() {
          var d = new Date().getTime();
          if (window.performance && typeof window.performance.now === "function") {
            d += performance.now(); //use high-precision timer if available
          }
          var uuid = "xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx".replace(
            /[xy]/g,
            function(c) {
              var r = (d + Math.random() * 16) % 16 | 0;
              d = Math.floor(d / 16);
              return (c == "x" ? r : (r & 0x3) | 0x8).toString(16);
            }
          );
          return uuid;
        },
    regist.vue

    vue请求图片验证码

    <template>
      <div>
          
          <p><img @click="refresh()" :src="'http://127.0.0.1:8000/user/generate_image_code/' + uuid" /></p>
          <p>验证码<input type="text" v-model="code"></p>
          
      </div>
    </template>
    
    <script>
    import axios from 'axios'
    export default {
      name:"regist",
      data() {
        return {
          code:'',
          uuid:'',
        }
      },
      methods: {
        generate_uuid: function() {
          var d = new Date().getTime();
          if (window.performance && typeof window.performance.now === "function") {
            d += performance.now(); //use high-precision timer if available
          }
          var uuid = "xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx".replace(
            /[xy]/g,
            function(c) {
              var r = (d + Math.random() * 16) % 16 | 0;
              d = Math.floor(d / 16);
              return (c == "x" ? r : (r & 0x3) | 0x8).toString(16);
            }
          );
          return uuid;
        },
        refresh(){
          this.uuid = this.generate_uuid()
        }
      },
      mounted() {
        this.uuid = this.generate_uuid()
      },
    }
    </script>
    code.vue

    带图片验证码的用户注册

    <template>
      <div>
          <p>用户名:<input type="text" v-model="name"></p>
          <p>密码:<input type="password" v-model="pwd"></p>
          <p>手机号:<input type="text" v-model="phone"></p>
          <p>邮箱:<input type="email" v-model="email"></p>
          <p><img @click="refresh()" :src="'http://127.0.0.1:8000/user/generate_image_code/' + uuid" /></p>
          <p>验证码<input type="text" v-model="code" @blur="check"></p>
          <p><button @click="regist_user()">注册</button></p>
    
      </div>
    </template>
    
    <script>
    import axios from 'axios'
    export default {
      name:"regist",
      data() {
        return {
          code:'',
          name:'',
          pwd:'',
          phone:'',
          email:'',
          uuid:'',
          is_ok:false
    
        }
      },
      methods: {
        generate_uuid: function() {
          var d = new Date().getTime();
          if (window.performance && typeof window.performance.now === "function") {
            d += performance.now(); //use high-precision timer if available
          }
          var uuid = "xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx".replace(
            /[xy]/g,
            function(c) {
              var r = (d + Math.random() * 16) % 16 | 0;
              d = Math.floor(d / 16);
              return (c == "x" ? r : (r & 0x3) | 0x8).toString(16);
            }
          );
          return uuid;
        },
        refresh(){
          this.uuid = this.generate_uuid()
        },
        check(){
          let post_data = new FormData()
          post_data.append('generate_image_id',this.uuid)
          post_data.append('user_code',this.code)
          axios({
            url: 'http://127.0.0.1:8000/user/check/',
            method: 'post',
            data: post_data,
          }).then(res=>{
            console.log(res.data)
            if (res.data.code==200){
              this.is_ok=true
            }else{
              this.is_ok=false
            }  
          })
          
        },
        regist_user(){
          if(this.is_ok==true){
            var form_data = new FormData()
            form_data.append("username",this.name)
            form_data.append("password",this.pwd)
            form_data.append("phone",this.phone)
            form_data.append("email",this.email)
            axios({
              url: 'http://127.0.0.1:8000/user/users/',
              method: 'post',
              data: form_data,
            }).then(res=>{
              console.log(res.data)
              sessionStorage.setItem("jwt_token",res.data.token)
              alert("注册成功")
    
            })        
          }else{
            alert("注册前请输入正确的用户码")
          }
          
    
        }
      },
      mounted() {
        this.uuid = this.generate_uuid()
      },
    }
    </script>
    regist.vue

     在Django项目中添加

    """
    Django settings for shiyanloupro project.
    
    Generated by 'django-admin startproject' using Django 2.2.7.
    
    For more information on this file, see
    https://docs.djangoproject.com/en/2.2/topics/settings/
    
    For the full list of settings and their values, see
    https://docs.djangoproject.com/en/2.2/ref/settings/
    """
    
    import os
    
    # Build paths inside the project like this: os.path.join(BASE_DIR, ...)
    BASE_DIR = os.path.dirname(os.path.dirname(os.path.abspath(__file__)))
    
    
    # Quick-start development settings - unsuitable for production
    # See https://docs.djangoproject.com/en/2.2/howto/deployment/checklist/
    
    # SECURITY WARNING: keep the secret key used in production secret!
    SECRET_KEY = 'f84z(fu2k-n^*38fn+o5xbx0wyxq*hrk-rs7__75p0ux$x8s2*'
    
    # SECURITY WARNING: don't run with debug turned on in production!
    DEBUG = True
    
    ALLOWED_HOSTS = []
    
    
    # Application definition
    
    INSTALLED_APPS = [
        'django.contrib.admin',
        'django.contrib.auth',
        'django.contrib.contenttypes',
        'django.contrib.sessions',
        'django.contrib.messages',
        'django.contrib.staticfiles',
        'rest_framework',
        'corsheaders',
        'rest_framework.authtoken',
        'userapp'
    ]
    
    MIDDLEWARE = [
        'django.middleware.security.SecurityMiddleware',
        'django.contrib.sessions.middleware.SessionMiddleware',
        'corsheaders.middleware.CorsMiddleware',
        'django.middleware.common.CommonMiddleware',
        # 'django.middleware.csrf.CsrfViewMiddleware',
        'django.contrib.auth.middleware.AuthenticationMiddleware',
        'django.contrib.messages.middleware.MessageMiddleware',
        'django.middleware.clickjacking.XFrameOptionsMiddleware',
    ]
    
    ROOT_URLCONF = 'shiyanloupro.urls'
    
    TEMPLATES = [
        {
            'BACKEND': 'django.template.backends.django.DjangoTemplates',
            'DIRS': [os.path.join(BASE_DIR, 'templates')]
            ,
            'APP_DIRS': True,
            'OPTIONS': {
                'context_processors': [
                    'django.template.context_processors.debug',
                    'django.template.context_processors.request',
                    'django.contrib.auth.context_processors.auth',
                    'django.contrib.messages.context_processors.messages',
                ],
            },
        },
    ]
    
    WSGI_APPLICATION = 'shiyanloupro.wsgi.application'
    
    
    # Database
    # https://docs.djangoproject.com/en/2.2/ref/settings/#databases
    
    DATABASES = {
        'default': {
            'ENGINE': 'django.db.backends.mysql',
            'HOST': '127.0.0.1',  # 数据库主机
            'PORT': 3306,  # 数据库端口
            'USER': 'root',  # 数据库用户名
            'PASSWORD': 'root',  # 数据库用户密码
            'NAME': 'shiyanlou'  # 数据库名字
        }
    }
    
    # Password validation
    # https://docs.djangoproject.com/en/2.2/ref/settings/#auth-password-validators
    
    AUTH_PASSWORD_VALIDATORS = [
        {
            'NAME': 'django.contrib.auth.password_validation.UserAttributeSimilarityValidator',
        },
        {
            'NAME': 'django.contrib.auth.password_validation.MinimumLengthValidator',
        },
        {
            'NAME': 'django.contrib.auth.password_validation.CommonPasswordValidator',
        },
        {
            'NAME': 'django.contrib.auth.password_validation.NumericPasswordValidator',
        },
    ]
    
    
    # Internationalization
    # https://docs.djangoproject.com/en/2.2/topics/i18n/
    
    LANGUAGE_CODE = 'en-us'
    
    TIME_ZONE = 'UTC'
    
    USE_I18N = True
    
    USE_L10N = True
    
    USE_TZ = True
    
    
    # Static files (CSS, JavaScript, Images)
    # https://docs.djangoproject.com/en/2.2/howto/static-files/
    
    STATIC_URL = '/static/'
    
    CORS_ALLOW_CREDENTIALS = True
    CORS_ORIGIN_ALLOW_ALL = True
    CORS_ORIGIN_WHITELIST = ()
    CORS_ALLOW_METHODS = (
        'DELETE',
        'GET',
        'OPTIONS',
        'PATCH',
        'POST',
        'PUT',
        'VIEW',
    )
    
    CORS_ALLOW_HEADERS = (
        'XMLHttpRequest',
        'X_FILENAME',
        'accept-encoding',
        'authorization',
        'content-type',
        'dnt',
        'origin',
        'user-agent',
        'x-csrftoken',
        'x-requested-with',
        'Pragma',
    )
    
    FONTS_DIRS =  os.path.join(BASE_DIR, 'fonts',)
    CACHES = {
        "default": {
            "BACKEND": "django_redis.cache.RedisCache",
            "LOCATION": "redis://127.0.0.1:6379/1",
            "OPTIONS": {
                "CLIENT_CLASS": "django_redis.client.DefaultClient",
            },
        }
    }
    AUTH_USER_MODEL='userapp.User'
    REST_FRAMEWORK = {
        # 身份认证
        'DEFAULT_AUTHENTICATION_CLASSES': (
            'rest_framework_jwt.authentication.JSONWebTokenAuthentication',
            'rest_framework.authentication.SessionAuthentication',
            'rest_framework.authentication.BasicAuthentication',
        ),
    
        #全局配置接口权限
        # 'DEFAULT_PERMISSION_CLASSES': (
        #         'rest_framework.permissions.IsAuthenticated',
        #     ),
    
    }
    
    import datetime
    
    JWT_AUTH = {
        'JWT_AUTH_HEADER_PREFIX': 'JWT',
        'JWT_EXPIRATION_DELTA': datetime.timedelta(days=1),
        'JWT_RESPONSE_PAYLOAD_HANDLER':
            'users.views.jwt_response_payload_handler',  # 重新login登录返回函数
    }
    setting.py
    from django.contrib import admin
    from django.urls import path
    from . import views
    urlpatterns = [
        path("generate_image_code/<str:generate_image_id>/",views.generate_image_code),
        path("check/",views.CheckCode.as_view()),
        path("users/",views.UserView.as_view()),
    
    ]
    user/urls.py
    from rest_framework_jwt.settings import api_settings
    from rest_framework import serializers
    from userapp.models import User
    
    
    class UserSerializer(serializers.Serializer):
        id  =serializers.IntegerField(read_only=True)
        username = serializers.CharField()
        password = serializers.CharField()
        phone = serializers.CharField()
        email = serializers.CharField()
        token = serializers.CharField(read_only=True)
    
        def create(self, data):
            user = User.objects.create(**data)
            #数据库里密码的加密(固定的步骤)
            user.set_password(data.get('password'))
            user.save()
    
            # 补充生成记录登录状态的token  固定的格式,用过来生成jwt的token
            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)
    
            #把token发放在user里返回
            user.token = token
            return user
    user/serializers.py
    1 from django.db import models
    2 from django.contrib.auth.models import AbstractUser
    3 # Create your models here.
    4 
    5 class User(AbstractUser):
    6     username = models.CharField(max_length=64, unique=True)
    7     password = models.CharField(max_length=255)
    8     phone = models.CharField(max_length=64)
    9     email = models.CharField(max_length=64)
    user/model.py
     1 from django.http.response import HttpResponse
     2 from PIL import Image, ImageDraw, ImageFont
     3 from shiyanloupro.settings import *
     4 from django.core.cache import cache
     5 from rest_framework.views import APIView
     6 from rest_framework.response import Response
     7 from .serializers import UserSerializer
     8 import random,os,io
     9 
    10 
    11 def generate_image_code(request, generate_image_id):
    12     '''
    13         本地图片验证码生成函数
    14     '''
    15     bgcolor = (random.randrange(20, 100), random.randrange(
    16         20, 100), random.randrange(20, 100))
    17     width = 110
    18     height = 40
    19     # 创建画面对象
    20     im = Image.new('RGB', (width, height), bgcolor)
    21     # 创建画笔对象
    22     draw = ImageDraw.Draw(im)
    23     # 调用画笔的point()函数绘制噪点
    24     for i in range(0, 100):
    25         xy = (random.randrange(0, width), random.randrange(0, height))
    26         fill = (random.randrange(0, 255), 255, random.randrange(0, 255))
    27         draw.point(xy, fill=fill)
    28     # 定义验证码的备选值
    29     str = '1234567890QWERTYUIOPASDFGHJKLZXCVBNMqwertyuiopasdfghjklzxcvbnm'
    30     # 随机选取4个值作为验证码
    31     rand_str = ''
    32     for i in range(0, 4):
    33         rand_str += str[random.randrange(0, len(str))]
    34     # 构造字体对象
    35 
    36     fonts_files = os.path.join(
    37         FONTS_DIRS, 'SourceCodePro-Bold.ttf')
    38     font = ImageFont.truetype(fonts_files, 30)
    39     # 构造字体颜色
    40     fontcolor1 = (255, random.randrange(0, 255), random.randrange(0, 255))
    41     fontcolor2 = (255, random.randrange(0, 255), random.randrange(0, 255))
    42     fontcolor3 = (255, random.randrange(0, 255), random.randrange(0, 255))
    43     fontcolor4 = (255, random.randrange(0, 255), random.randrange(0, 255))
    44     # 绘制4个字
    45     draw.text((5, 2), rand_str[0], font=font, fill=fontcolor1)
    46     draw.text((25, 2), rand_str[1], font=font, fill=fontcolor2)
    47     draw.text((50, 2), rand_str[2], font=font, fill=fontcolor3)
    48     draw.text((75, 2), rand_str[3], font=font, fill=fontcolor4)
    49     # 释放画笔
    50     del draw
    51     # 存入缓存,用于做进一步验证,并设置超时时间为10分组
    52     cache.set(generate_image_id,rand_str,60*10)
    53     buf = io.BytesIO()
    54     # 将图片保存在内存中,文件类型为png
    55     im.save(buf, 'png')
    56     # 将内存中的图片数据返回给客户端,MIME类型为图片png!
    57     return HttpResponse(buf.getvalue(), 'image/png')
    58 
    59 
    60 
    61 class CheckCode(APIView):
    62     def post(self,request):
    63         generate_image_id = request.data.get('generate_image_id',"")
    64         data_code = cache.get(generate_image_id)
    65         user_code = request.data.get('user_code',"")
    66         if data_code and user_code:
    67             print(data_code,user_code)
    68             if data_code.lower() == user_code.lower():
    69                 return Response({'code':200})
    70         return Response({'code':201})
    71 
    72 
    73 
    74 class UserView(APIView):
    75     def post(self,request):
    76         serializer = UserSerializer(data=request.data)
    77         if serializer.is_valid():
    78             serializer.save()
    79             return Response(serializer.data, status=200)
    80         return Response(serializer.errors, status=200)
    user/views.py
  • 相关阅读:
    IE678下,select 诡异的样式
    跟着我一步一步的搭建一个基于springcloud的微服务实例
    关于Future踩过的坑
    Apache下的SocketClient的使用
    Jaxb处理泛型,转化成xml字符串
    Linux Centos虚拟机扩容
    docker 搭建zookeeper集群和kafka集群
    sysbench 数据库性能测试工具的使用
    docker 容器技术
    自己手写实现Dubbo
  • 原文地址:https://www.cnblogs.com/jxhp/p/13440104.html
Copyright © 2020-2023  润新知