————————————————————————前言——————————————————————————
数据库中的密码是不安全的, 一旦恶意用户得到数据库的访问权, 那我们的密码就会被泄露, 利益必然受损。
所以我们不应该在数据库中直接存储密码, 而是要存储密码对应的散列值。
————————————————————————————————————————————————————
一. 修改models.py脚本中的User类
from werkzeug.security import generate_password_hash, check_password_hash
from . import db
class User(db.Model):
#...
password_hash = db.Column(db.String(128)) #增加一个hash密码字段
@property
def password(self):
raise AttributeError('password is not a readable attribute') #实例访问password属性时,会引发属性错误
@password.setter
def password(self, password): #实例设置password属性时, 就会调用该函数生成密码hash值
self.password_hash = generate_password_hash(password)
def verify_password(self, password): #验证密码是否符合之前存储的hash值
return check_password_hash(self.password_hash, password)
@property 和 @password.setter修饰器在文末会有介绍。
二. 增加一个测试脚本test_user_model.py
import unittest
from app.models import User
class userModelTestCase(unittest.TestCase):
def test_password_setter(self): #测试hash值是否设置成功
u = User(password='cat')
self.assertTrue(u.password_hash is not None)
def test_no_password_getter(self): #测试实例是否不能访问password属性
u = User(password='cat')
with self.asssertRaise(AttributeError):
u.password
def test_password_verification(self): #测试密码验证函数
u = User(password='cat')
self.assertTrue(u.verify_password('cat'))
self.assertFalse(u.verify_password('dog'))
def test_password_salts_are_random(self): #测试密码加盐是随机的: 不同用户的相同密码, hash值也不同
u = User(password='cat')
u2 = User(password='cat')
self.assertTrue(u.password_hash != u2.password_hash)
测试效果展示:
三. 修饰器property和password.setter
1. 我们先举一个简单的小例子
class Person(object): #Person类
def getAge(self):
return self._age
def setAge(self, age):
self._age = age
>p = Person() #创建Person实例
>p.setAge(3) #设置p.age为3
>p.getAge() #获得p.age
3
调用函数设置和获得age值不免显得麻烦, 如果能用p.age = 3设置属性值, p.age获得属性值就会显得简单易懂。
我们可以用@property装饰器实现上述操作, 把getter方法变成属性, 并且@property本身又创建了另一个装饰器@password.setter, 把setter方法变成属性赋值:
class Person(object):
@property
def age(self):
return self._age
@age.setter
def age(self, age):
self._age = age
>p = Person() #创建实例
>p.age = 3 #相当于调用p.age(3)
>p.age #相当于调用p.age()
3
回到我们的User类:
from werkzeug import generate_password_hash, check_password_hash
from . import db
class User(db.Model):
#...
password_hash = db.Column(db.String(128))
@property
def password(self):
raise attributeError('password is not a readable attribute')
@password.setter
def password(self, password):
self.password_hash = generate_password_hash(password)
User类中我们把password设置为只读属性, 试图访问该属性会引发错误
>u = User() #创建实例
>u.password = 'cat' #相当于调用u.password('cat'), 把u.password_hash赋值为generate_password_hash('cat')
>u.password #相当于调用u.password(), 引发attributeError