第14章 实战
验证码
目的
区分人与机器
全称叫作:全自动区分计算机和人类的图灵测试(Completely Automated Public Turing test to tell Computers and Humans Apart,简称CAPTCHA),俗称验证码
为了解决暴力登录,垃圾广告等问题。
工作原理
常见的图形验证码是与web中的session
相关联的,在一个session
开始时,在需要使用验证码的地方会生成一个与当前会话相关的验证码,用户识别出验证码后通过填写表单将数据提交给服务器,服务器端会验证此次会话中的验证码是否正确。具体来说,其工作流程如图1所示:
验证码发送逻辑
手机验证码
手机验证码,代码
from rest_framework import serializers
class SmsSerializer(serializers.Serializer):
mobile = serializers.CharField(max_length=11)
def validate_mobile(self, mobile):
"""验证手机号"""
# 手机是否注册
if User.objects.filter(mobile=mobile).count():
raise serializers.ValidationError("用户已经存在")
# 手机号码是否合法
if not re.match(setings.REGEX_MOBILE, mobile):
raise serializers.ValidationError("手机号码不合法")
# 发送时间限制
one_mintes_ago = datetime.now()-timedelta(minutes=1)
if VerifyCode.objects.filter(add_time__gt=one_mintes_ago, mobile=mobile):
raise serializers.ValidationError("距离上次发送未超过60秒")
return mobile
图形验证码
验证码目录
utils
captcha
_init_.py
Cookie-Regular.ttf
Courgette-Regular.ttf
euphorig.tff
LHANDW.TTF
Lobster-Regular.ttf
verdana.ttf
Pillow 介绍
PIL (Python Image Library) 是 Python 平台处理图片的事实标准,兼具强大的功能和简洁的 API。
安装
pip install Pillow
创建验证码
import random
import string
#Image:一个画布
#ImageDraw:一个画笔
#ImageFont:画笔的字体
from PIL import Image,ImageDraw,ImageFont
class Captcha(object):
"""captcha验证码"""
number = 4 # 生成几位数的验证码
size = (100, 30) # 验证码图片的宽度和高度
fontsize = 25 # 验证码字体大小
line_number = 2 # 加入干扰线的条数
# 构建一个验证码源文本
SOURCE = list(string.ascii_letters)
for index in range(0, 10):
SOURCE.append(str(index))
# 绘制干扰线
@classmethod
def __gene_line(cls, draw, width, height):
begin = (random.randint(0, width), random.randint(0, height))
end = (random.randint(0, width), random.randint(0, height))
draw.line([begin, end], fill=cls.__gene_random_color(), width=2)
# 绘制干扰点
@classmethod
def __gene_points(cls, draw, point_chance, width, height):
chance = min(100, max(0, int(point_chance))) # 大小限制在[o, 100]
for w in range(width):
for h in range(height):
tmp = random.randint(0, 100)
if tmp > 100 - chance:
draw.point((w, h), fill=cls.__gene_random_color())
# 生成随机的颜色
@classmethod
def __gene_random_color(cls, start=0, end=255):
random.seed()
return (random.randint(start, end), random.randint(start, end), random.randint(start, end))
# 随机选择一个字体
@classmethod
def _gene_random_font(cls):
fonts = [
# "Courgette-Regular.ttf",
# "Lobster.ttf",
# "verdana.ttf",
"Lobster.ttf",
]
font = random.choice(fonts)
print(font)
return "utils/captcha/" + font
# 随机生成一个字符串(包括英文和数字)
@classmethod
def gene_text(cls, number):
# number是生成验证码的位数
return "".join(random.sample(cls.SOURCE, number))
# 生成验证码
@classmethod
def gene_graph_captcha(cls):
width, height = cls.size # 验证码图片宽和高
# R:Red(红色)0-255
# G:G(绿色)0-255
# B:B(蓝色)0-255
# A:Alpha(透明度)
image = Image.new("RGBA", (width, height), cls.__gene_random_color(0, 100))
# 验证码的字体
font = ImageFont.truetype(cls._gene_random_font(), cls.fontsize)
# 创建画笔
draw = ImageDraw.Draw(image)
# 生成字符串
text = cls.gene_text(cls.number)
# 获取字体的尺寸
font_width, font_height = font.getsize(text)
# 填充字符串
draw.text(((width - font_width) / 2, (height - font_height) / 2), text, font=font,
fill=cls.__gene_random_color(150, 255))
# 绘制干扰线
for x in range(0, cls.line_number):
cls.__gene_line(draw, width, height)
# 绘制噪点
cls.__gene_points(draw, 10, width, height)
with open("captcha.png", "wb")as fp:
image.save(fp)
return (text, image)
传输验证码
from django.http import HttpResponse
def graph_captcha():
#获取验证码
text,image=Captcha.gene_graph_captcha()
#BytesIo:字节流
out=BytesIO()
image.save(out,"png")
out.seek(0)
# flask 响应
# resp=make_response(out.read()) # flask 响应对象函数
# resp.content_type="image/png"
# django 响应
resp = HttpResponse(content_type='image/png')
resp.content = out_obj.read()
return resp
# urls.py
urlpatterns = [
path('register/', views.UserRegisterView.as_view(), name='register'),
path('captcha/', views.graph_captcha, name='captcha')
]
使用redis/memcache缓存验证码
import memcache
cache=memcache.Client(['127.0.0.1:112111], debug=True)
def set(key, value, timeout=60):
return cache.set(key, value, timeout)
def get(key):
return cache.get(Key)
def delete(key):
return cache.delete(key)
redis 库介绍
redis提供两个类
- StrictRedis:实现了大部分官方的命令,并使用官方的语法
- redis(StrictRedis):向后兼容旧版本的
redis-py
redis连接实例是线程安全的,可以直接将redis连接实例设置为一个全局变量,直接使用。如果需要另一个Redis实例(or Redis数据库)时,就需要重新创建redis连接实例来获取一个新的连接。同理,python的redis没有实现select命令。
import redis
# host是redis主机,需要redis服务端和客户端都启动 redis默认端口是6379
# decode_responses=True 写入的kv对中的value为str类型,如果不加这个参数,写入的为字节类型
redis_cache = redis.Redis(host='localhost', port=6379, decode_responses=True)
redis_cache.set('name', 'junxi') # key是"foo" value是"bar" 将键值对存入redis缓存
print(redis_cache['name'])
print(redis_cache.get('name')) # 取出键name对应的值
print(type(redis_cache.get('name')))
def set(key, value, timeout=60):
return redis_cache.set(key, vlaue, timeout)
def get(key):
return redis_cache.get(key)
def delete(key):
return redis_cache.
使用django-redis
django-redis是一个使 Django 支持 Redis cache/session 后端的全功能组件.
django依赖
django-redis 3.8.x 支持 django 1.4, 1.5, 1.6, 1.7 (或许会有 1.8)
django-redis 4.4.x 支持 django 1.6, 1.7, 1.8, 1.9 和 1.10
redis server支持
django-redis 3.x.y 支持 redis-server 2.6.x 或更高
django-redis 4.x.y 支持 redis-server 2.8.x 或更高
其他依赖
所有版本的 django-redis 基于 redis-py >= 2.10.0.
OAuth
原理
反馈
+--------------+ +---------------+
| | <------------+ | |
| 本地应用 | | 第三方应用 |
| +---------------> | |
+------^-------+ 引导用户跳入 +-------+-----^-+
| | |
| | |
| | |
| | |
| 请求第三方登录 响应授权页 | | 用户授权登录
| | |
| | |
| +-------------+ | |
| | <-------+ |
| | 用户 | |
+----------+ +---------------+
+-------------+
开放平台
- 微博开放平台
- 微信开放平台
- 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的是有地理意义的坐标:城市,河流,山峰