寫在前面:此時實戰(zhàn)的項目參考<<Flask Web開發(fā)>>一書項目示例

Flask Web.jpg
使用的模塊
- Flask-Login:管理已登入用戶的會話
- Werkzeug: 生成密碼hash值并校驗
- itsdangerous:生成并核對激活用戶鏈接
-
為提高密碼的安全性,數(shù)據(jù)庫中存儲密碼的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方法驗證用戶輸入的密碼是否與數(shù)據(jù)庫中的密碼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() # 表單類還有兩個自定義的驗證函數(shù),以方法的實行實現(xiàn)。如果表單類中定義了以validate_開頭且后面跟著字段名的方法, # 這個方法就和驗證函數(shù)一起調(diào)用 # 保證了注冊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發(fā)送一個默認郵件,用戶需要點擊認證之后才能登入;實現(xiàn)方式很簡單,確認郵件中添加鏈接
http://your_domain/auth/confirm/<id>, 這個id是用戶表的主鍵,視圖函數(shù)接收到這個主鍵并把用戶狀態(tài)更新;為了安全,使用
itsdangerous生成用戶id的安全令牌;# 用戶類中添加方法 class User(UserMixin, db.Model): ... # 根據(jù)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當沒有驗證的用戶訪問頁面時,需要將他們重定向到一個統(tǒng)一的頁面,
Flask提供了4個請求鉤子: - before_first_request:注冊一個函數(shù),在處理第一個請求之前運行。 - before_request:注冊一個函數(shù),在每次請求之前運行。 - after_request:注冊一個函數(shù),如果沒有未處理的異常拋出,在每次請求之后運行。 - teardown_request:注冊一個函數(shù),即使有未處理的異常拋出,也在每次請求之后運行。 在請求鉤子函數(shù)和視圖函數(shù)之間共享數(shù)據(jù)一般使用上下文全局變量 g。例如,before_ request 處理程序可以從數(shù)據(jù)庫中加載已登錄用戶,并將其保存到 g.user 中。隨后調(diào)用視 圖函數(shù)時,視圖函數(shù)再使用 g.user 獲取用戶。這里可以使用
before_request來實現(xiàn), 對于藍本來說,before_request鉤子只能應用到屬于藍本的請求上,若想對全集請求生效,還需要使用before_app_request裝飾器:# 驗證用戶是否已經(jīng)通過郵件確認 @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'))