Flask Web.jpg写在前面:此时实战的项目参考<<Flask Web开发>>一书项目示例
使用的模块
- Flask-Login:管理已登入用户的会话
- Werkzeug: 生成密码hash值并校验
- itsdangerous:生成并核对激活用户链接
-
为提高密码的安全性,数据库中存储密码的hash值
from werkzeug.security import generate_password_hash, check_password_hash 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) # 利用verify_password方法验证用户输入的密码是否与数据库中的密码hash值一致 def verify_password(self, password): return check_password_hash(self.password_hash, password)
-
用户登入使用Flask-Login模块,详情请见另一篇博客;
-
注册用户
注册用户使用Flask-WTF表单, 其中可以很方便地使用WTF表单字段的验证功能:
class RegistrationForm(Form): email = StringField( 'Email', validators=[ DataRequired(), Length( 1, 64), Email()]) username = StringField( 'Username', validators=[ DataRequired(), Length( 1, 64), Regexp( '^[A-Za-z0-9_.]*$', 0, 'Username must have only letters, numbers, dots or underscores')]) password = StringField( 'Password', validators=[ DataRequired(), EqualTo( 'password2', message='Passwords must match.')]) password2 = PasswordField('Confirm password', validators=[DataRequired()]) submit = SubmitField() # 表单类还有两个自定义的验证函数,以方法的实行实现。如果表单类中定义了以validate_开头且后面跟着字段名的方法, # 这个方法就和验证函数一起调用 # 保证了注册email的唯一 def validate_email(self, field): if User.query.filter_by(email=field.data).first(): raise ValidationError("Email already registered.") # 保证了注册username唯一 def validate_username(self, field): if User.query.filter_by(username=field.data).first(): raise ValidationError("Username already use.")
-
确认账户
很多使用用户注册成功后,会像用户的email发送一个默认邮件,用户需要点击认证之后才能登入;实现方式很简单,确认邮件中添加链接
http://your_domain/auth/confirm/<id>
, 这个id
是用户表的主键,视图函数接收到这个主键并把用户状态更新;为了安全,使用
itsdangerous
生成用户id
的安全令牌;# 用户类中添加方法 class User(UserMixin, db.Model): ... # 根据id, 生成token def generate_confirmation_token(self, expiration=3600): s = Serializer(current_app.config['SECRET_KEY'], expiration) return s.dumps({'confirm': self.id}) # 验证token是否与id一致,一致则更新user对象的属性 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
当没有验证的用户访问页面时,需要将他们重定向到一个统一的页面,
Flask提供了4个请求钩子: - before_first_request:注册一个函数,在处理第一个请求之前运行。 - before_request:注册一个函数,在每次请求之前运行。 - after_request:注册一个函数,如果没有未处理的异常抛出,在每次请求之后运行。 - teardown_request:注册一个函数,即使有未处理的异常抛出,也在每次请求之后运行。 在请求钩子函数和视图函数之间共享数据一般使用上下文全局变量 g。例如,before_ request 处理程序可以从数据库中加载已登录用户,并将其保存到 g.user 中。随后调用视 图函数时,视图函数再使用 g.user 获取用户。
这里可以使用
before_request
来实现, 对于蓝本来说,before_request
钩子只能应用到属于蓝本的请求上,若想对全集请求生效,还需要使用before_app_request
装饰器:# 验证用户是否已经通过邮件确认 @auth.before_app_request def before_request(): if current_user.is_authenticated: current_user.ping() if not current_user.confirmed \ and request.endpoint \ and request.endpoint[:5] != 'auth.' \ and request.endpoint != 'static': return redirect(url_for('auth.unconfirmed'))
网友评论