第14章 实战

验证码

目的

区分人与机器

全称叫作:全自动区分计算机和人类的图灵测试(Completely Automated Public Turing test to tell Computers and Humans Apart,简称CAPTCHA),俗称验证码

为了解决暴力登录,垃圾广告等问题。

工作原理

常见的图形验证码是与web中的session相关联的,在一个session开始时,在需要使用验证码的地方会生成一个与当前会话相关的验证码,用户识别出验证码后通过填写表单将数据提交给服务器,服务器端会验证此次会话中的验证码是否正确。具体来说,其工作流程如图1所示:

验证码发送逻辑

手机验证码

第14章 实战 - 图1

手机验证码,代码

  1. from rest_framework import serializers
  2. class SmsSerializer(serializers.Serializer):
  3. mobile = serializers.CharField(max_length=11)
  4. def validate_mobile(self, mobile):
  5. """验证手机号"""
  6. # 手机是否注册
  7. if User.objects.filter(mobile=mobile).count():
  8. raise serializers.ValidationError("用户已经存在")
  9. # 手机号码是否合法
  10. if not re.match(setings.REGEX_MOBILE, mobile):
  11. raise serializers.ValidationError("手机号码不合法")
  12. # 发送时间限制
  13. one_mintes_ago = datetime.now()-timedelta(minutes=1)
  14. if VerifyCode.objects.filter(add_time__gt=one_mintes_ago, mobile=mobile):
  15. raise serializers.ValidationError("距离上次发送未超过60秒")
  16. return mobile

图形验证码

验证码目录

  1. utils
  2. captcha
  3. _init_.py
  4. Cookie-Regular.ttf
  5. Courgette-Regular.ttf
  6. euphorig.tff
  7. LHANDW.TTF
  8. Lobster-Regular.ttf
  9. verdana.ttf

Pillow 介绍

PIL (Python Image Library) 是 Python 平台处理图片的事实标准,兼具强大的功能和简洁的 API。

安装

  1. pip install Pillow

创建验证码

  1. import random
  2. import string
  3. #Image:一个画布
  4. #ImageDraw:一个画笔
  5. #ImageFont:画笔的字体
  6. from PIL import Image,ImageDraw,ImageFont
  7. class Captcha(object):
  8. """captcha验证码"""
  9. number = 4 # 生成几位数的验证码
  10. size = (100, 30) # 验证码图片的宽度和高度
  11. fontsize = 25 # 验证码字体大小
  12. line_number = 2 # 加入干扰线的条数
  13. # 构建一个验证码源文本
  14. SOURCE = list(string.ascii_letters)
  15. for index in range(0, 10):
  16. SOURCE.append(str(index))
  17. # 绘制干扰线
  18. @classmethod
  19. def __gene_line(cls, draw, width, height):
  20. begin = (random.randint(0, width), random.randint(0, height))
  21. end = (random.randint(0, width), random.randint(0, height))
  22. draw.line([begin, end], fill=cls.__gene_random_color(), width=2)
  23. # 绘制干扰点
  24. @classmethod
  25. def __gene_points(cls, draw, point_chance, width, height):
  26. chance = min(100, max(0, int(point_chance))) # 大小限制在[o, 100]
  27. for w in range(width):
  28. for h in range(height):
  29. tmp = random.randint(0, 100)
  30. if tmp > 100 - chance:
  31. draw.point((w, h), fill=cls.__gene_random_color())
  32. # 生成随机的颜色
  33. @classmethod
  34. def __gene_random_color(cls, start=0, end=255):
  35. random.seed()
  36. return (random.randint(start, end), random.randint(start, end), random.randint(start, end))
  37. # 随机选择一个字体
  38. @classmethod
  39. def _gene_random_font(cls):
  40. fonts = [
  41. # "Courgette-Regular.ttf",
  42. # "Lobster.ttf",
  43. # "verdana.ttf",
  44. "Lobster.ttf",
  45. ]
  46. font = random.choice(fonts)
  47. print(font)
  48. return "utils/captcha/" + font
  49. # 随机生成一个字符串(包括英文和数字)
  50. @classmethod
  51. def gene_text(cls, number):
  52. # number是生成验证码的位数
  53. return "".join(random.sample(cls.SOURCE, number))
  54. # 生成验证码
  55. @classmethod
  56. def gene_graph_captcha(cls):
  57. width, height = cls.size # 验证码图片宽和高
  58. # R:Red(红色)0-255
  59. # G:G(绿色)0-255
  60. # B:B(蓝色)0-255
  61. # A:Alpha(透明度)
  62. image = Image.new("RGBA", (width, height), cls.__gene_random_color(0, 100))
  63. # 验证码的字体
  64. font = ImageFont.truetype(cls._gene_random_font(), cls.fontsize)
  65. # 创建画笔
  66. draw = ImageDraw.Draw(image)
  67. # 生成字符串
  68. text = cls.gene_text(cls.number)
  69. # 获取字体的尺寸
  70. font_width, font_height = font.getsize(text)
  71. # 填充字符串
  72. draw.text(((width - font_width) / 2, (height - font_height) / 2), text, font=font,
  73. fill=cls.__gene_random_color(150, 255))
  74. # 绘制干扰线
  75. for x in range(0, cls.line_number):
  76. cls.__gene_line(draw, width, height)
  77. # 绘制噪点
  78. cls.__gene_points(draw, 10, width, height)
  79. with open("captcha.png", "wb")as fp:
  80. image.save(fp)
  81. return (text, image)

传输验证码

  1. from django.http import HttpResponse
  2. def graph_captcha():
  3. #获取验证码
  4. text,image=Captcha.gene_graph_captcha()
  5. #BytesIo:字节流
  6. out=BytesIO()
  7. image.save(out"png")
  8. out.seek(0)
  9. # flask 响应
  10. # resp=make_response(out.read()) # flask 响应对象函数
  11. # resp.content_type="image/png"
  12. # django 响应
  13. resp = HttpResponse(content_type='image/png')
  14. resp.content = out_obj.read()
  15. return resp
  16. # urls.py
  17. urlpatterns = [
  18. path('register/', views.UserRegisterView.as_view(), name='register'),
  19. path('captcha/', views.graph_captcha, name='captcha')
  20. ]

使用redis/memcache缓存验证码

  1. import memcache
  2. cache=memcache.Client(['127.0.0.1:112111], debug=True)
  3. def set(key, value, timeout=60):
  4. return cache.set(key, value, timeout)
  5. def get(key):
  6. return cache.get(Key)
  7. def delete(key):
  8. return cache.delete(key)

redis 库介绍

redis提供两个类

  • StrictRedis:实现了大部分官方的命令,并使用官方的语法
  • redis(StrictRedis):向后兼容旧版本的redis-py

redis连接实例是线程安全的,可以直接将redis连接实例设置为一个全局变量,直接使用。如果需要另一个Redis实例(or Redis数据库)时,就需要重新创建redis连接实例来获取一个新的连接。同理,python的redis没有实现select命令。

  1. import redis
  2. # host是redis主机,需要redis服务端和客户端都启动 redis默认端口是6379
  3. # decode_responses=True 写入的kv对中的value为str类型,如果不加这个参数,写入的为字节类型
  4. redis_cache = redis.Redis(host='localhost', port=6379, decode_responses=True)
  5. redis_cache.set('name', 'junxi') # key是"foo" value是"bar" 将键值对存入redis缓存
  6. print(redis_cache['name'])
  7. print(redis_cache.get('name')) # 取出键name对应的值
  8. print(type(redis_cache.get('name')))
  9. def set(key, value, timeout=60):
  10. return redis_cache.set(key, vlaue, timeout)
  11. def get(key):
  12. return redis_cache.get(key)
  13. def delete(key):
  14. return redis_cache.

使用django-redis

django-redis是一个使 Django 支持 Redis cache/session 后端的全功能组件.

django依赖

  1. django-redis 3.8.x 支持 django 1.4, 1.5, 1.6, 1.7 (或许会有 1.8)
  2. django-redis 4.4.x 支持 django 1.6, 1.7, 1.8, 1.9 1.10

redis server支持

  1. django-redis 3.x.y 支持 redis-server 2.6.x 或更高
  2. django-redis 4.x.y 支持 redis-server 2.8.x 或更高

其他依赖

所有版本的 django-redis 基于 redis-py >= 2.10.0.

OAuth

原理

  1. 反馈
  2. +--------------+ +---------------+
  3. | | <------------+ | |
  4. | 本地应用 | | 第三方应用 |
  5. | +---------------> | |
  6. +------^-------+ 引导用户跳入 +-------+-----^-+
  7. | | |
  8. | | |
  9. | | |
  10. | | |
  11. | 请求第三方登录 响应授权页 | | 用户授权登录
  12. | | |
  13. | | |
  14. | +-------------+ | |
  15. | | <-------+ |
  16. | | 用户 | |
  17. +----------+ +---------------+
  18. +-------------+

开放平台

  • 微博开放平台
  • 微信开放平台
  • QQ开放平台

多帐号登录

user表

column type note
id int 用户id
password varchar 口令
status int 状态
created_at

扩展用户表:user_login

column type note
id int 绑定id
user_id int 用户id
type varchar phone/useranme/email/wechat_openid
account varchar 手机/用户名/email
create_at

地图

名词:

POI Point of interesting

可以翻译为兴趣点,就是在地图上任何非地理意义的有意义的点:比如商店,酒吧,加油站,医院,车站等。不属于poi的是有地理意义的坐标:城市,河流,山峰