首页 > 其他分享 >实战项目-美多商城(二)用户注册,登录

实战项目-美多商城(二)用户注册,登录

时间:2023-02-23 17:02:44浏览次数:44  
标签:用户注册 self random height captcha 美多 width image 商城

图片验证码

  • 依赖Pillow库,先安装
pip install Pillow
  • scripts(项目根目录),新建 captcha包
    里面再新建fonts目录,用来存放字体文件
- actionj.ttf
- Arial.ttf
- Georgia.ttf
### captcha.captcha
# -*- coding: utf-8 -*-

# refer to `https://bitbucket.org/akorn/wheezy.captcha`

import random
import string
import os.path
from io import BytesIO

from PIL import Image
from PIL import ImageFilter
from PIL.ImageDraw import Draw
from PIL.ImageFont import truetype


class Bezier:
    def __init__(self):
        self.tsequence = tuple([t / 20.0 for t in range(21)])
        self.beziers = {}

    def pascal_row(self, n):
        """ Returns n-th row of Pascal's triangle
        """
        result = [1]
        x, numerator = 1, n
        for denominator in range(1, n // 2 + 1):
            x *= numerator
            x /= denominator
            result.append(x)
            numerator -= 1
        if n & 1 == 0:
            result.extend(reversed(result[:-1]))
        else:
            result.extend(reversed(result))
        return result

    def make_bezier(self, n):
        """ Bezier curves:
            http://en.wikipedia.org/wiki/B%C3%A9zier_curve#Generalization
        """
        try:
            return self.beziers[n]
        except KeyError:
            combinations = self.pascal_row(n - 1)
            result = []
            for t in self.tsequence:
                tpowers = (t ** i for i in range(n))
                upowers = ((1 - t) ** i for i in range(n - 1, -1, -1))
                coefs = [c * a * b for c, a, b in zip(combinations,
                                                      tpowers, upowers)]
                result.append(coefs)
            self.beziers[n] = result
            return result


class Captcha(object):
    def __init__(self):
        self._bezier = Bezier()
        self._dir = os.path.dirname(__file__)
        # self._captcha_path = os.path.join(self._dir, '..', 'static', 'captcha')

    @staticmethod
    def instance():
        if not hasattr(Captcha, "_instance"):
            Captcha._instance = Captcha()
        return Captcha._instance

    def initialize(self, width=200, height=75, color=None, text=None, fonts=None):
        # self.image = Image.new('RGB', (width, height), (255, 255, 255))
        self._text = text if text else random.sample(string.ascii_uppercase + string.ascii_uppercase + '3456789', 4)
        self.fonts = fonts if fonts else \
            [os.path.join(self._dir, 'fonts', font) for font in ['Arial.ttf', 'Georgia.ttf', 'actionj.ttf']]
        self.width = width
        self.height = height
        self._color = color if color else self.random_color(0, 200, random.randint(220, 255))

    @staticmethod
    def random_color(start, end, opacity=None):
        red = random.randint(start, end)
        green = random.randint(start, end)
        blue = random.randint(start, end)
        if opacity is None:
            return red, green, blue
        return red, green, blue, opacity

    # draw image

    def background(self, image):
        Draw(image).rectangle([(0, 0), image.size], fill=self.random_color(238, 255))
        return image

    @staticmethod
    def smooth(image):
        return image.filter(ImageFilter.SMOOTH)

    def curve(self, image, width=4, number=6, color=None):
        dx, height = image.size
        dx /= number
        path = [(dx * i, random.randint(0, height))
                for i in range(1, number)]
        bcoefs = self._bezier.make_bezier(number - 1)
        points = []
        for coefs in bcoefs:
            points.append(tuple(sum([coef * p for coef, p in zip(coefs, ps)])
                                for ps in zip(*path)))
        Draw(image).line(points, fill=color if color else self._color, width=width)
        return image

    def noise(self, image, number=50, level=2, color=None):
        width, height = image.size
        dx = width / 10
        width -= dx
        dy = height / 10
        height -= dy
        draw = Draw(image)
        for i in range(number):
            x = int(random.uniform(dx, width))
            y = int(random.uniform(dy, height))
            draw.line(((x, y), (x + level, y)), fill=color if color else self._color, width=level)
        return image

    def text(self, image, fonts, font_sizes=None, drawings=None, squeeze_factor=0.75, color=None):
        color = color if color else self._color
        fonts = tuple([truetype(name, size)
                       for name in fonts
                       for size in font_sizes or (65, 70, 75)])
        draw = Draw(image)
        char_images = []
        for c in self._text:
            font = random.choice(fonts)
            c_width, c_height = draw.textsize(c, font=font)
            char_image = Image.new('RGB', (c_width, c_height), (0, 0, 0))
            char_draw = Draw(char_image)
            char_draw.text((0, 0), c, font=font, fill=color)
            char_image = char_image.crop(char_image.getbbox())
            for drawing in drawings:
                d = getattr(self, drawing)
                char_image = d(char_image)
            char_images.append(char_image)
        width, height = image.size
        offset = int((width - sum(int(i.size[0] * squeeze_factor)
                                  for i in char_images[:-1]) -
                      char_images[-1].size[0]) / 2)
        for char_image in char_images:
            c_width, c_height = char_image.size
            mask = char_image.convert('L').point(lambda i: i * 1.97)
            image.paste(char_image,
                        (offset, int((height - c_height) / 2)),
                        mask)
            offset += int(c_width * squeeze_factor)
        return image

    # draw text
    @staticmethod
    def warp(image, dx_factor=0.27, dy_factor=0.21):
        width, height = image.size
        dx = width * dx_factor
        dy = height * dy_factor
        x1 = int(random.uniform(-dx, dx))
        y1 = int(random.uniform(-dy, dy))
        x2 = int(random.uniform(-dx, dx))
        y2 = int(random.uniform(-dy, dy))
        image2 = Image.new('RGB',
                           (width + abs(x1) + abs(x2),
                            height + abs(y1) + abs(y2)))
        image2.paste(image, (abs(x1), abs(y1)))
        width2, height2 = image2.size
        return image2.transform(
            (width, height), Image.QUAD,
            (x1, y1,
             -x1, height2 - y2,
             width2 + x2, height2 + y2,
             width2 - x2, -y1))

    @staticmethod
    def offset(image, dx_factor=0.1, dy_factor=0.2):
        width, height = image.size
        dx = int(random.random() * width * dx_factor)
        dy = int(random.random() * height * dy_factor)
        image2 = Image.new('RGB', (width + dx, height + dy))
        image2.paste(image, (dx, dy))
        return image2

    @staticmethod
    def rotate(image, angle=25):
        return image.rotate(
            random.uniform(-angle, angle), Image.BILINEAR, expand=1)

    def captcha(self, path=None, fmt='JPEG'):
        """Create a captcha.

        Args:
            path: save path, default None.
            fmt: image format, PNG / JPEG.
        Returns:
            A tuple, (name, text, StringIO.value).
            For example:
                ('fXZJN4AFxHGoU5mIlcsdOypa', 'JGW9', '\x89PNG\r\n\x1a\n\x00\x00\x00\r...')

        """
        image = Image.new('RGB', (self.width, self.height), (255, 255, 255))
        image = self.background(image)
        image = self.text(image, self.fonts, drawings=['warp', 'rotate', 'offset'])
        image = self.curve(image)
        image = self.noise(image)
        image = self.smooth(image)
        name = "".join(random.sample(string.ascii_lowercase + string.ascii_uppercase + '3456789', 24))
        text = "".join(self._text)
        out = BytesIO()
        image.save(out, format=fmt)
        if path:
            image.save(os.path.join(path, name), fmt)
        return name, text, out.getvalue()

    def generate_captcha(self):
        self.initialize()
        return self.captcha("")


captcha = Captcha.instance()

if __name__ == '__main__':
    # 返回 name,text, image
    ('9md3IwpPBLyXO7YuJFEMjnzK', '9YMI', b'\xff\xd8\xff\......')
    print(captcha.generate_captcha())

### captcha.captcha_demo
### 测试脚本demo.py

# from .captcha import captcha
# __main__ is not a package报错:引用相对路径造成的,修改成以下这句即可

from captcha import captcha


# name是文件名称ID
# text 文本验证码
# image 验证码图片二进制流
name,text, image = captcha.generate_captcha()

print('名称是',name) # TdLDlWyaXZMPptHi6jNu4Jqh
print('验证码的内容是:', text) # UXZR

# 将验证码图片二进制流 存入图片(点击刚生成的图片,看效果)
with open('demo.png', 'wb') as f:
    f.write(image)


- 图片demo测试完毕以后,就要写后端接口,生成图片,返回给前端显示

  • 验证码前/后端的逻辑

- 前端生成随机字符串码(image_code_id),发送给后端

- 后端接收image_code_id,根据 验证码脚本 生成 图片文本text 和 图片二进制码image

- 后端把 image_code_id 和 text 存入redis 并把 image 返回给前端展示

### register.html
......
<li>
    <label>图形验证码:</label>
    <input type="text" v-model="image_code" @blur="check_image_code" name="pic_code" id="pic_code" class="msg_input">
    <!--动态src属性,用户单击图片的时候,就刷新src属性-->
    <img :src="image_code_url" @click="generate_image_code" alt="图形验证码" class="pic_code">
    <span v-show="error_image_code" class="error_tip">{{ error_image_code_message }}</span>
</li>
......
<scripts>
// 生成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;
},
// 生成一个图片验证码的编号,并设置页面中图片验证码img标签的src属性
generate_image_code: function(){
    // 生成一个编号
    // 严格一点的使用uuid保证编号唯一, 不是很严谨的情况下,也可以使用时间戳
    this.image_code_id = this.generate_uuid();
    // 设置页面中图片验证码img标签的src属性
    this.image_code_url = this.host + "/image_codes/" + this.image_code_id + "/";
}
......
</scripts>
### apps.users.urls
from django.conf.urls import url
from . import views

urlpatterns = [
    ......
    # 验证码(接收image_code_id)
    url('^image_codes/(?P<image_code_id>[\w-]+)/$', views.ImageCodeView.as_view()),
]

### views
from rest_framework.views import APIView
from rest_framework.response import Response
from django.http import HttpResponse

from scripts.captcha.captcha import captcha

from django_redis import get_redis_connection
......
class ImageCodeView(APIView):
    def get(self,request,image_code_id):
        _,text,image = captcha.generate_captcha()
        redis_conn = get_redis_connection('verify_codes')
        redis_conn.set(image_code_id,text,ex=60)
        return HttpResponse(image,content_type='images/jpg')

  • 至此,刷新注册页面,查看验证码效果

标签:用户注册,self,random,height,captcha,美多,width,image,商城
From: https://www.cnblogs.com/qinganning/p/17148684.html

相关文章