确认账户
对于某些特定类型的程序,有必要确认注册时用户提供的信息是否正确。常见要求是能通过提供的电 子邮件地址与用户取得联系。
为验证电子邮件地址, 用户注册后,程序会立即发送一封确认邮件。新账户先被标记成未激活状态,用户点击邮件中的链接后,才能激活。账户确认过程中,往往会要求用户点击一个包含确认令牌的特殊 URL 链接。
1. 使用itsdangerous生成确认令牌
确认邮件中最简单的确认链接是 http://www.example.com/auth/confirm/<id> 这种形式的URL,其 中 id 是数据库分配给用户的数字 id。用户点击链接后,处理这个路由的视图函数就将收到的用户id作为参数进行确认,然后将用户状态更新为已激活。
但是这种方式不是很安全,只要用户能判断确认链接的格式,就可以随便指定URL中的数字,从而确认任意账户。解决方法是把 URL 中的 id换成将相同信息安全加密后得到的令牌。
itsdangerous 提供了多种生成令牌的方法。其中, TimedJSONWebSignatureSerializer 类生成具有 过期时间的 JSON Web 签名(JSON Web Signatures, JWS)。这个类的构造函数接收的参数是一个密钥,在 Flask 程序中可使用 SECRET_KEY 设置。
dumps()方法为指定的数据生成一个加密签名,然后再对数据和签名进行序列化,生成令牌字符串。 expires_in 参数设置令牌的过期时间,单位为秒。 为了解码令牌, 序列化对象提供了 loads()方法,其唯一的参数是令牌字符串。这个方法会检验签 名和过期时间, 如果通过,返回原始数据。如果提供给 loads() 方法的令牌不正确或过期了,则抛 出异常。
我们可以将这种生成和检验令牌的功能可添加到 User 模型中。
【 app/models.py:确认用户账户】
from itsdangerous import TimedJSONWebSignatureSerializer as Serializer
from flask import current_app
class User(UserMixin, db.Model):
confirmed = db.Column(db.Boolean, default=False)
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)
return True
generate_confirmation_token() 方法生成一个令牌,有效期默认为一小时。
confirm()方法检验令牌,如果检验通过,则把新添加的 confirmed 属性设为 True。
由于模型中新加入了一个列用来保存账户的确认状态,因此要生成并执行一个新的数据库迁移。
2. 发送确认邮件
/register 路由把新用户添加到数据库中后,会重定向到 /index。在重定向之前,这个路由需要发 送确认邮件。
首先编辑邮件:
【confirm.txt】
Dear {
{ user.username }},
Welcome to Flasky!
To confirm your account please click on the following link:
{
{ url_for('auth.confirm', token=token, _external=True) }}
Sincerely,
The Flasky Team
Note: replies to this email address are not monitored.
【confirm.html】
<p>Dear {
{ user.username }},</p>
<p>Welcome to <b>Flasky</b>!</p>
<p>To confirm your account please <a href="{
{ url_for('auth.confirm', token=token, _external=True) }}">click
here</a>.</p>
<p>Alternatively, you can paste the following link in your browser's address bar:</p>
<p>{
{ url_for('auth.confirm', token=token, _external=True) }}</p>
<p>Sincerely,</p>
<p>The Flasky Team</p>
<p><small>Note: replies to this email address are not monitored.</small></p>
默认情况下, url_for() 生成相对 URL,例如 url_for('auth.confirm', token='abc') 返回的字符串是 '/auth/confirm/abc'。这不能够在电子邮件中发送的正确URL。
_external=True 参数要求程序生成完整的 URL,其中包含协议(http://或 https:// )、主机名和端口。
【app/auth/views.py:能发送确认邮件的注册路由】