一、什么是werkzeug
werkzeug 官方的介绍说是一个 WSGI 工具包,不是一个web服务器,也不是一个web框架,它可以作为一个 Web 框架的底层库,因为它封装好了很多 Web 框架的东西,例如 Request,Response 等等;例如我最常用的 Flask 框架就是一 Werkzeug 为基础开发的。
二、Werkzeug实现密码校验功能
使用Werkzeug实现密码散列,最关键的是使用其中security模块的generater_password_hash()和check_password_hash()。
from werkzeug.security import generate_password_hash,check_password_hash
class User(db.Model):
__tablename__ = 'users'
id = db.Column(db.Integer, primary_key=True)#id列
username = db.Column(db.String(64), unique=True, index=True) #username列
role_id = db.Column(db.Integer, db.ForeignKey('roles.id')) #role_id列
password_hash = db.Column(db.String(128)) #密码hash列
@property
def password(self): #访问password属性
raise AttributeError('password is not a readable attribute')
# 对保存到数据库中的密码进行加盐
# 什么是盐?
# 通过一个随机字符串或指定字符串与原有字符串进行混淆处理,等到一个新的字符串,这个过程称为加盐处理。
@password.setter
def password(self,password): #设置password属性
self.password_hash = generate_password_hash(password) #默认加盐了
# 输入密码时进行校验
def verify_password(self,password):#验证密码
return check_password_hash(self.password_hash,password)
def __repr__(self):
return '<user r="">' % self.username</user>
1、generate_password_hash(password, method=pbkdf2:sha1, slat_length=8)
method指定哈希变化的算法,默认sha1,salt_length指定‘盐的长度’,默认是8位。password是必须指定的。
返回计算得出的128位哈希值
2、check_password_hash(hash,password)
hash是密码散列值(一般存于数据库),password是输入需比较的密码。
返回的是True或者False
三、local 模块
1、简介
在local
模块中,Werkzeug实现了类似Python标准库中threading.local
的功能。threading.local
是线程局部变量,也就是每个线程的私有变量,具有线程隔离性,可以通过线程安全的方式获取或者改变线程中的变量。参照threading.local
,Werkzeug实现了比thread.local
更多的功能。
总结起来: 以上文档解释了对于“并发”问题,多线程并不是唯一的方式,在Python中还有“协程”。“协程”的一个显著特点在于是一个线程执行,一个线程可以存在多个协程。也可以理解为:协程会复用线程。对于
WSGI
应用来说,如果每一个线程处理一个请求,那么thread.local
完全可以处理,但是如果每一个协程处理一个请求,那么一个线程中就存在多个请求,用thread.local
变量处理起来会造成多个请求间数据的相互干扰。
对于上面问题,Werkzeug库解决的办法是local
模块。local
模块实现了四个类:
Local
LocalStack
LocalProxy
LocalManager
本文重点介绍前两个类的实现。
2、Local类
Local
类能够用来存储线程的私有变量。在功能上这个threading.local
类似。与之不同的是,Local
类支持Python的协程。在Werkzeug库的local模块中,Local
类实现了一种数据结构,用来保存线程的私有变量,对于其具体形式,可以参考它的构造函数:
class Local(object):
__slots__ = ('__storage__', '__ident_func__')
def __init__(self):
object.__setattr__(self, '__storage__', {})
object.__setattr__(self, '__ident_func__', get_ident)
从上面类定义可以看出,Local
类具有两个属性:__storage__
和__ident_func__
。从构造函数来看,__storage__
是一个字典,而__ident_func__
是一个函数,用来识别当前线程或协程。
1. __ident_func__
关于当前线程或协程的识别,local
模块引入get_ident
函数。如果支持协程,则从greenlet
库中导入相关函数,否则从thread
库中导入相关函数。调用get_ident
将返回一个整数,这个整数可以确定当前线程或者协程。
try:
from greenlet import getcurrent as get_ident
except ImportError:
try:
from thread import get_ident
except ImportError:
from _thread import get_ident
2. __storage__
__storage__
是一个字典,用来存储不同的线程/协程,以及这些线程/协程中的变量。以下是一个简单的多线程的例子,用来说明__storage__
的具体结构。
import threading
from werkzeug.local import Local
l = Local()
l.__storage__
def add_arg(arg, i):
l.__setattr__(arg, i)
for i in range(3):
arg = 'arg' + str(i)
t = threading.Thread(target=add_arg, args=(arg, i))
t.start()
l.__storage__
上面的例子,具体分析为:
- 首先,代码创建了一个
Local
的实例l
,并且访问它的__storage__
属性。由于目前还没有数据,所以l.__storage__
的结果为{}
; - 代码创建了3个线程,每个线程均运行
add_arg(arg, i)
函数。这个函数会为每个线程创建一个变量,并对其赋值; - 最后,再次访问
l.__storage__
。这次,l
实例中将包含3个线程的信息。其结果为:
{20212: {'arg0': 0}, 20404: {'arg1': 1}, 21512: {'arg2': 2}}
从以上结果可以看出,__storage__
这个字典的键表示不同的线程(通过get_ident
函数获得线程标识数值),而值表示对应线程中的变量。这种结构将不同的线程分离开来。当某