在学习Flask-Web开发这本书的第八章遇到了问题,就是在数据库模型中添加确认用户账户的功能时,有这样一段代码:
class User(UserMixin,db.Model):
__tablename__ = 'users'
id = db.Column(db.Integer,primary_key=True)
email = db.Column(db.String(64),unique=True,index=True)
username=db.Column(db.String(64),unique=True,index=True)
role_id = db.Column(db.Integer,db.ForeignKey('roles.id'))
password_hash = db.Column(db.String(128))
confirmed = db.Column(db.Boolean,default=False)
@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)
def verify_password(self,password):
return check_password_hash(self.password_hash,password)
def generate_confirmation_token(self,expiration=3600):
s=Serializer(current_app.config['SECRET_KEY'],expiration)
return s.dumps({'confirm':self.id})
def confirm(self,token):
s=Serializer(current_app.config['SECRET_KEY'])
try:
data=s.loads(token)
except:
return False
if data.get("confirm") != self.id:
return False
self.confirmed = True
db.session.add(self)
db.session.commit()
return True
def __repr__(self):
return '<User %r>' % self.username
confirm()方法验证令牌。
其中,有一段是这样的:if data.get("confirm") != self.id,正常情况下data返回的不就是原数据么({'confirm':self.id}),加这段代码的意义是什么?
书中有解释:confirm()方法还检查令牌中的id是否和存储在current_user中的已登录用户匹配。这里的current_user是否就是指正在进行验证的这个用户?对于Flask来说,尚未确认的用户也算已登录的用户吗,即current_user.is_authentiacated为True。将代码改为
if data.get("confirm") != current_user.get_id():
可否?
以及最后一句话,添加这段代码:if data.get("confirm") != self.id,即使恶意用户知道如何生成签名令牌,也无法确认别人的账户。账户应该是指注册或者登录时的email吧,这段代码确认了confirm数据是否为用户唯一id号,和用户账户(email)有什么关系?
要注意的是這裡的
token
和SECRET_KEY
。這裡的data經過了一次
data=s.loads(token)
,如果token和SECRET_KEY不正确的话,get
得到的id也是不正確的,這樣就起到了驗證的作用。這裡的帳號指的是每個帳號自己的id,就算惡意使用者成功偽造了token,因為使用者的id號不正確,這裡無法成功驗證。
登入和確認是兩個屬性,這兩個之間沒有耦合的關係,沒有登入的帳號也可以是確認過的,所以沒有確認的帳號也可以是登入狀態的。