二.图形验证码
Posted dbslinux
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了二.图形验证码相关的知识,希望对你有一定的参考价值。
# 图形验证码: - 作用:注册页面 - 实现思路: - 生成4位字符串--产生随机数 - 绘制图片--python的PIL包,但这里我不用它,我用第三包captcha来绘制 - 响应,--告诉浏览器--指定数据类型为image/png - pip install Pillow - 解压拷贝capcha到libs中 - 实现: - 配置路由规则 - 定义视图,调用capcha的方法 - 视图的逻辑: - 调用captcha生成图片数据 - 保存文本到redis中 - 输出图片数据
1.创建验证应用:
归根结底本质上就是显示一张图片,html中加一个img标签并指定src就行,但是注意它不是一张固定图片,思路:直接去请求一个视图,视图中去画一张图片出来,并在图片中写文字,再把图片保存到response响应对象中就可返回给浏览器.但有个问题,浏览器怎么知道你返回的是一张图片?--mime type数据格式---我们告诉浏览器我给你的二进制是图片imgae/jpg
capcha包中的capcha.py文件解说:
#!/usr/bin/env python # -*- 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 #image相当于一张纸 from PIL import ImageFilter from PIL.ImageDraw import Draw #draw相当于一根笔 from PIL.ImageFont import truetype #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 x1a x00x00x00 ...‘) """ 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__‘: print(captcha.generate_captcha()) #这样调用就能生成验证码数据 ‘‘‘ 怎么用呢?--它里边有这个__main__函数,所以直接单击这个文件右键---run就可输出如下了 (‘oNFHu6a9MyWvRtDQmTwhCXJY‘, ‘M5JK‘, ====>图片中的字符 图片的二进制数据==》b‘xffxd8xffxe0x00x10JFIFx00x01x01x00x00x01x00x01x00x00xffxdbx00Cx00x08x06x06x07x06x05x08x07x07x07 x08 x0cx14 x0cx0bx0bx0cx19x12x13x0fx14x1dx1ax1fx1ex1dx1ax1cx1c $.‘ ",#x1cx1c(7),01444x1f‘9=82<.342xffxdbx00Cx01 x0cx0bx0cx18 x182!x1c!22222222222222222222222222222222222222222222222222xffxc0x00x11x08x00Kx00xc8x03x01"x00x02x11x01x03x11x01xffxc4x00x1fx00x00x01x05x01x01x01x01x01x01x00x00x00x00x00x00x00x00x01x02x03x04x05x06x07x08 x0bxffxc4x00xb5x10x00x02x01x03x03x02x04x03x05x05x04x04x00x00x01}x01x02x03x00x04x11x05x12!1Ax06x13Qax07"qx142x81x91xa1x08#Bxb1xc1x15Rxd1xf0$3brx82 x16x17x18x19x1a%&‘()*456789:CDEFGHIJSTUVWXYZcdefghijstuvwxyzx83x84x85x86x87x88x89x8ax92x93x94x95x96x97x98x99x9axa2xa3xa4xa5xa6xa7xa8xa9xaaxb2xb3xb4xb5xb6xb7xb8xb9xbaxc2xc3xc4xc5xc6xc7xc8xc9xcaxd2xd3xd4xd5xd6xd7xd8xd9xdaxe1xe2xe3xe4xe5xe6xe7xe8xe9xeaxf1xf2xf3xf4xf5xf6xf7xf8xf9xfaxffxc4x00x1fx01x00x03x01x01x01x01x01x01x01x01x01x00x00x00x00x00x00x01x02x03x04x05x06x07x08 x0bxffxc4x00xb5x11x00x02x01x02x04x04x03x04x07x05x04x04x00x01x02wx00x01x02x03x11x04x05!1x06x12AQx07aqx13"2x81x08x14Bx91xa1xb1xc1 #3Rxf0x15brxd1 x16$4xe1%xf1x17x18x19x1a&‘()*56789:CDEFGHIJSTUVWXYZcdefghijstuvwxyzx82x83x84x85x86x87x88x89x8ax92x93x94x95x96x97x98x99x9axa2xa3xa4xa5xa6xa7xa8xa9xaaxb2xb3xb4xb5xb6xb7xb8xb9xbaxc2xc3xc4xc5xc6xc7xc8xc9xcaxd2xd3xd4xd5xd6xd7xd8xd9xdaxe2xe3xe4xe5xe6xe7xe8xe9xeaxf2xf3xf4xf5xf6xf7xf8xf9xfaxffxdax00x0cx03x01x00x02x11x03x11x00?x00xf7xba(xa2xa8x02x8a(xa0x02x91Y[xeexb0?CU%x9exebxedxe9x02xdaxeexb6exf9xa6xddxf7Oxa6)xf6Vx16xfa|Mx1dxbax95Vmxc4x12O4x89xbdxdex85x9a(xaarxd9x06xbfx8exf1xaee@x83x1e^xec!xfax8ax06xd9rx8ax01x04dx1cx8a)x8c(xaa(xb3Gxa8I4x97xcamx88xc2xc3x81xf2x9fxd5xc0xeaxddx18x1fxa1xa4$xee:x8a23x8cxd1LaEWxbcxbaxfbx1dxabOxe5I.xdfxe0x8cdx9ax82xd2[xf9xaex8cxb2$ifxc8 )x1fxbcx07xdex95xc9rWxb1~x8a(xa6PQA x0cx93x8axc9xfexd2x96xf6xf0Exa6xc9x13,/xb6xe3x9x1fJMxd8NIx1axd4QE1x85x14Q@x05x14Q@x05x14Q@x05Pxd5gxb9xb7xb6x0fjxf0+x03x96xf3x8ex06*xfdpx972xcbsxe2x13-xebox82)xbcxaf(xf4Pzx1aLx89xbb+x1bvx9e/xd3.Fx1dxde‘x07x042xfexb4xe3xe2xcbx01|mUesxd02xaeAxad/xecxfbx15x8dxb1ox12xab. xda:W+xa7hwz_x8ax17jx17xb3lx95nxa0fxa7TKsV:8xb5xdd:xe1x18}xa0Fxd8xe4?x04Qx06x95nxfasxdbxc9<xb70xcawnwxc9xfc >xed4xc6YMxc0xb7xcax0fx9cxb0x1f.}k/xc3xf3xc9%xb5xe6x9cx93axe1?xbax93xaex14xf44xfdFxed{3xa1x86$x82x14x8a1x84Ax85x19xedYx1axfdxe3Dxf6x96ix9fxf4x99x02xb1x07x04x0exf5ax9bQxb1xb0x89U>xddqxbbx0exd9 xc7xadTt[xefx13xc7xb8x8cZGxb8xafxa3x1ax06xdex96Dx16zFx8dwspx88xd3Hxf16x1c30x00xd6xb0xd2xadx11@@xc9x81x81xb5x8dpx1axa6xadp<Mqx12xcb9x886x02Bpjkxabx88xe3xx8dxc5xdexa1n_xa6xe6xc8x15*Hxca5cxafx91xabx1cx8dcxe3Xxacxe1xb9x95xe3uxfdxe0vxcf5xd9Wx9ahxb7x96x16zxfcxd7xb7xd7x85xc2x8cFxe7x92xdfZxdex9bxc7x10xc8xc6=:xca{x993x81x81x80}xe8SKqBxb4bx9f3xeauxa7x80Mrqxf8xd1x7fxb6xfexc1-xafx967xecxdcZxb3xcfx89xbcOx93x8d+x8fxfaxe6kx9dxd7-xf55xb9MFxf2xd4[xb4xa7 xafxafxf4xa5*x9dx88xa9x89xd2xf0xbfxdczx9dxd7xdbx8cxd0}x93xc9xf2xb3xfbxddxfdqxedR^Oxf6{Ydx0cxa1x95Ix195xc4iPxebxfe!xb3x8eCxaax08xedxc1xc1xd9xc3x0cUxabx8fx04xdexafxefxf5x99dxdbxd3p4xf9x9bxd5#EVm^1ex8d:x19uxdd7xedx17x9ax94x89xe6x12x02Fxc1p3XxbaNxa64?x12xcbdnwYxe4xa9g==xf3Rxx7fxc2xd6xf7xf1xacxf7x17 xf0xcaSxe4l xc7xd6xf4x03gxaexadx8dxaaxc9 p x96xe4x9ax86xe5dxecc9TINxdfx89xd4xdfxxd1^xf5 xd2xcaxcbxce>`~sxe8+oLxd7xadxf5 x9axd8xabCtx9fz‘xebY6xb6Vxda$,.l"Hxa1Mxcbtxc4x12xcfxf4xacOx0bxdaxdfjxbaxecxbax98x9cxa2xa3xfcxccFwx0fJxbefx9exa6x8axa4xd4x92zxb6z=x14QZx1dAEx14Px01Ex14Px01wx8a!xfb>xa0x19cbxb7Qxecxf9Gxf1x8ex84xd7cYx9axedxb3Oxa73xc6xa0xcbx11x12‘x1ex94x9axba"jxe8xa6x936xadxe17;x88x9bxcb xedxeax18Ux7f kxadxa8[xfdx92xe3xfd|#x00x9fxe2x14x~xe9Mxfcxf0x84x0bx15xc2xf9xca=xfax1axe5xf58o4xfdBxecxdbxcax91y2x99x17‘ xcfxa7xafxd2xa1xbbjg)8xdaFx82xdfxecxf1xbdxcdxadxd0x0fx05xc3x05daxc1xf4xadx8bxa4x1aGx89xedgE mpxbeSx01xc0xcfjxf3xd8xefxae&xbe7Lxad5xcewx06xee1[:xcdxcexbfxaaxdbDnxa0)lx14H x0e>xa4xd4)xe8sxc6xbaxb3i]xdfCxbcxbcxf1x0ex95dxa4xcbyx1eAxc1U99xae2xcbxc4xd3xa5xfdxfcxf6vr<xe7*Oxf0x81xd2xb6xf4xaf ixc7Kx8ep<xcbx99cxdc$~@$zW<xfaUxd5x86xa5-x9bI3xb99Dx84mx0fx9fxe5Txf9x8d&xeb;7xa7xa0x96>x1bxd5xf5;xd6xbciRxddxddxb7x16xddxcf>x82xafj~x15]1#xbexb8x99xefx11[xf7xcaxd9xe7=xc5T[;xed&xf6xdeSx14xf6xe5xe4x01x07x99xbb>xc6xbax7fx16xeaxf1Yhx8dx16xe0exb8]xaa:xfdi(xc5+xb2cJx9ax8brxfcIxb4xefx0exe8f$xbax82xcdYdx8dxfcxf1xf8xd6xbdxbdx95xb5xaaxedx82x08xe3x19xcfxcaxb8xaax1ex1aYWxc3xf6x82Sx96xd9xc7xd3xb5/xf6xc3.xbfxfdx99-xb9Pxc9xba93x9dxdfxe1Z+$tGx92);Zxe6xadaxf8xb2xc7xedxdax0cxc0x0cxb4cxxfc+rx99,bXx9e6x19x0cx084xdaxbaxb1xa4x972ix9e}xe0-Ymxeedxd3xe58x12x1cxa7xd6xbdx10xf21^;ux14xbax17x88xceFxd3x14xbbx87xd35xebvwQxdexdaGqx13x06Gx82+:oK>x876x16Ox95xc1xeex8cOx0cxc7xe4xddxeaqx9cxe4NNx0fxa5nxb5xbc-:xccxd1xa9x91Fx03x11xc8x15xcdixbax83x1fx18xdexdbylx88xebx9fx98`x92;x8ax9bxc6wwVZTsxdaxc8xc8VAxb8xad]xd2W5SJ xf6(xf8xf6x1bxa9xadxa0xf2xf3xf6pxdf9xcfx19xf7xadx1dx03Hx9axc2xdaxd4xc3x>xcfxb3/x18xef‘xbejMx1exfax1fx11h[nx02x96exdb"xe7xf5xac}x0fQ}7xc4x0fxa3x19|xdbbHx8cxf5+xedSxa5xefxdcx8bGx99Oxb9xdaxd1Ex15xa1xd0x14QEx00x14QEx00x14x8dxf7x0fx19xe3xa5-x14x01xe53xffx00kxdejx93Egx0cxb1xb4x0exccx15O+xcf<xd2xe8xfaMxcexa5xaf,Zxa4wx0cx0f.[<xe3xd4xd7xa4Eipx9ax94x97x06xe1Lx0c0xb1x04x03x07xd75%xe4x12xcdlxebm(x82cxd2Mxb9xc5execxfaxb3x93xeaxd7|xd2mx9cxf7x884xcbm7LKx8b+Ex06#x86x089+x8csY:x0fx89xed_Lm.xfcxecxdc +x1ex98>xb5xd8xd8^Cvxb2[yxa6i`xf9e%qx93Xxbaxa7x81xec/x0bIlMxbcxa7x9e>xefxe5Mxa7xbcKx94exa4xa9xfdxc4Z[xfdxafxc3x92xabxdcxcbx18xb3sx86x89xb0Hx1dx07xd2xa2x8bQxd6xf5x1bx94x16xc2xd23xb7pxe7,x17xdex88|?wxa4xkPx85xa5Wgx1bx86xd3xd8Ukixb5x1bkdx9cxbdx8dxb0(x17xcex1cxb9x14j%}/xa1xad-xa4x16x08xf3xebwxc6xe5x9cmQx8cc=x80x15xccxddiw:xaexb3ogx0cmx1dxa2x8dxc8xa4xe7bxfbxfbxd4x96‘Rxd4xb5Yx80xcdxcbxe7jx95xf9x10zxe3xd6xbbx9d/M]:xdfc9x96Sxcbxc8zxb1xa2xca@xa2xaaxadxb4-[@xb6xd6xd1xc2x9fux14(xa96xa9mxdbF}qXxf7xbaxa5xf5xbdxe9xb6x8fOvWx18x8aaxcaxe7xfdxa1xd8Vx8dx9fxdaxbexcaxbflxf2xfcxffx00xe2xf2xfaUxa7xd0xddI7dOx90x0e x14xb5xe5xfe*xbexd4xed5xe7 q"xa09x8fixc0xc5wx9a&xabx0exa1xa5xc3/x9cx86Mxa08xcfCRxa4x9bhx88VRx93x8fcx98xf8x85`6Azxabxcev1xa4xf0%xfdxe4xa0xd9xabxc5xf6xxceX7xdfxfc+Cxc6xf7xb6x8dxa2xb4x02hxda]xc3 x0eMsx9e x91xed.nox042xcax88x98+x18xc995x0fJx874xedx1cBxb7]xcdxcdfxefxfbx17xc5x89x7f,,xf0xbc[rxa3xbdUxd7xb5xdfxedx9d*X#x89Sx07$3|xdfx95vf(5x1bXxda{pUx80mx92x0eGxd6xaaKxe1xfbx06xb7xxe1x88@xcfxffx00-#xe1x87xe3Vxd3:% 4xecxf4g%xa5hx1ax8cxfax0cRxd9Jxd6xd3x92Cx068xdcxb5xbfxa0xdixc5nnx98Ir:x11xd1kR{+x9fxb1Cx05xa5xd9x85xa3#22xee,=xeaxf2x82x14x02rqxd6x85x1bx0ex14xd2+Zxeax16xf7x93Mx14,Kxc2pxe0xa9x185jx90*xa9$(x04xf5xc0xa5xaa4Wxeax14QE1x85x14Q@x05x14Q@x05x14Q@x08x15Tx92x14x02zxe0Rxd1Ex006Hxd6Xx9a7x19Vx18"xb1xe2xf0xaex95x1bdxc0xe7#sx13x8axdaxa2x95x84xd2{x8cx8e(xe1@x91xa2xaax8ex00x03x15^;x1f/Qx92xefxedx12xb6xf1x8f(xb7xc8>x82xadxd1@4x98QEx14xc6`xxa7Axfexd9xb2x06%x1fix8fxeex13xdfxdaxbcxf1xb4Mjxd2Cx1axdbNx0fxfbx19xc1xafbxa2xa2Txd4x9dxcc*axe3Qxf3lxcf.xb0xf0Vxab|xeaxf7?xb9x8cxe0x92xe7$x8axf4-‘Ixb7xd2-x05xbdxb8xe3xa9cxd4x9axbfEx11x82x8exc3xa5Bx14xf6xdc(xa2x8axb3`xa2x8a(x00xa2x8a(x00xa2x8a(x03xffxd9‘) ‘‘‘
上述文件中,对象给我们创建好了,所以我们直接拿到这个对象调方法就好了,所以我接下来就写个视图,接收客户端的请求,来调类的方法,返回一个数据就完了。
我单独创建一个应用--专负责验证的,因为我这个验证在注册模块用到,但别的模块也会用到.
verifycations/urls.py中:
这个url不是随便写的,这个js当中写好的,直接复制用就行
from django.conf.urls import url
from . import views
import uuid
urlpatterns = [
url(‘^image_codes/(?P<uuid>[w-]+)/$‘, views.ImageCodeView.as_view()),
]
根urls.py中包含:
from django.contrib import admin from django.conf.urls import url,include urlpatterns = [ url(r‘^admin/‘, admin.site.urls), url(r‘^‘, include(‘users.urls‘)), url(‘^‘,include(‘verifycations.urls‘)), ]
views.py中:
from django.shortcuts import render from django.views import View from meiduo_mall.libs.captcha.captcha import captcha #导入captcha对象 from django_redis import get_redis_connection #连接redis from . import constants from django import http class ImageCodeView(View): def get(self, request, uuid): # 接收 # 验证 # 处理: # 1.生成图片的文本、数据 text, code, image = captcha.generate_captcha() # 2.保存图片文本,用于后续与用户输入值对比 redis_cli = get_redis_connection(‘image_code‘) #连接缓存中配的image_code缓存 redis_cli.setex(uuid, constants.IMAGE_CODE_EXPIRES, code)#往redis中写数据,键是uuid唯一标识,过期时间,值是code # 响应:输出图片数据 return http.HttpResponse(image, content_type=‘image/png‘)#告诉浏览器这是图片类型
去注册:dev.py中追加:
INSTALLED_APPS = [ .......‘verifycations.apps.VerifycationsConfig‘, ]
dev.py中增加图片验证的缓存项:
"image_code": { # 图形验证码 "BACKEND": "django_redis.cache.RedisCache", "LOCATION": "redis://127.0.0.1:6379/2", "OPTIONS": { "CLIENT_CLASS": "django_redis.client.DefaultClient", } },
新建verifycations/constants.py过期时间文件:
#图片验证码在redis中的过期时间,单位是秒 IMAGE_CODE_EXPIRES = 60 * 5
以上是关于二.图形验证码的主要内容,如果未能解决你的问题,请参考以下文章