overview
authentication and authorization is WebapplicationDevelopmentin important 组成部分. authentication is verificationuser身份 过程, 而authorization则 is 确定user可以访问哪些resource 过程. 本章节将介绍such as何 in Flaskapplicationinimplementationuserauthentication and authorizationfunctions, includingpassword哈希, sessionmanagement, rolepermissionetc. in 容.
1. password哈希 and securitystore
store明文password is 非常不security 做法. 我们应该usingpassword哈希algorithms将password转换 for 不可逆 哈希值, 然 after 只store哈希值. Flask-Bcrypt is a scale, 它providing了bcrypt哈希algorithms encapsulation, 方便我们 in Flaskapplicationinusing.
1.1 installationFlask-Bcrypt
pip install flask-bcrypt
1.2 configurationFlask-Bcrypt
from flask import Flask
from flask_bcrypt import Bcrypt
app = Flask(__name__)
app.config['SECRET_KEY'] = 'your-secret-key'
bcrypt = Bcrypt(app)
1.3 password哈希 and verification
我们可以usingFlask-Bcryptproviding generate_password_hashmethod来生成password哈希, usingcheck_password_hashmethod来verificationpassword:
# 生成password哈希
hashed_password = bcrypt.generate_password_hash('password').decode('utf-8')
# verificationpassword
if bcrypt.check_password_hash(hashed_password, 'password'):
print("password正确")
else:
print("passworderror")
1.4 in usermodelinusingpassword哈希
from flask import Flask
from flask_sqlalchemy import SQLAlchemy
from flask_bcrypt import Bcrypt
app = Flask(__name__)
app.config['SECRET_KEY'] = 'your-secret-key'
app.config['SQLALCHEMY_DATABASE_URI'] = 'sqlite:///site.db'
db = SQLAlchemy(app)
bcrypt = Bcrypt(app)
class User(db.Model):
id = db.Column(db.Integer, primary_key=True)
username = db.Column(db.String(20), unique=True, nullable=False)
email = db.Column(db.String(120), unique=True, nullable=False)
password = db.Column(db.String(60), nullable=False)
def set_password(self, password):
self.password = bcrypt.generate_password_hash(password).decode('utf-8')
def check_password(self, password):
return bcrypt.check_password_hash(self.password, password)
2. sessionmanagement and loginstatus
sessionmanagement is Webapplicationin用于跟踪userloginstatus mechanism. Flaskproviding了 in 置 sessionsupport, 我们可以usingsessionobject来store and 访问sessiondata.
2.1 configurationsessionkey
for 了security起见, 我们需要configuration一个随机 sessionkey:
app.config['SECRET_KEY'] = 'your-secret-key' # 应该using一个随机生成 string
2.2 loginfunctionsimplementation
from flask import render_template, url_for, flash, redirect, request, session
from app import app, db, bcrypt
from app.forms import LoginForm
from app.models import User
@app.route("/login", methods=['GET', 'POST'])
def login():
form = LoginForm()
if form.validate_on_submit():
user = User.query.filter_by(email=form.email.data).first()
if user and user.check_password(form.password.data):
# storeuserID to sessionin
session['user_id'] = user.id
flash('login成功!', 'success')
return redirect(url_for('home'))
else:
flash('login失败, 请check邮箱 and password', 'danger')
return render_template('login.html', title='login', form=form)
2.3 登出functionsimplementation
@app.route("/logout")
def logout():
# from sessionin移除userID
session.pop('user_id', None)
flash('已成功登出', 'success')
return redirect(url_for('home'))
2.4 loginstatuscheck
我们可以creation一个装饰器来checkuser is 否已login:
from functools import wraps
from flask import session, flash, redirect, url_for
def login_required(f):
@wraps(f)
def decorated_function(*args, **kwargs):
if 'user_id' not in session:
flash('请先login', 'warning')
return redirect(url_for('login'))
return f(*args, **kwargs)
return decorated_function
# using装饰器保护routing
@app.route("/dashboard")
@login_required
def dashboard():
return render_template('dashboard.html')
2.5 获取当 before user
我们可以creation一个function来获取当 before login user:
def get_current_user():
if 'user_id' in session:
return User.query.get(session['user_id'])
return None
# in routinginusing
@app.route("/profile")
@login_required
def profile():
user = get_current_user()
return render_template('profile.html', user=user)
3. Flask-Loginscale
Flask-Login is a 专门用于processinguserauthentication Flaskscale, 它providing了更完整 sessionmanagementfunctions, including记住我, login保护, 当 before user获取etc..
3.1 installationFlask-Login
pip install flask-login
3.2 configurationFlask-Login
from flask import Flask
from flask_login import Loginmanagementr
app = Flask(__name__)
app.config['SECRET_KEY'] = 'your-secret-key'
# configurationLoginmanagementr
login_manager = Loginmanagementr(app)
login_manager.login_view = 'login' # 设置login视graph
login_manager.login_message_category = 'info' # 设置loginmessageclass别
3.3 updateusermodel
我们需要updateusermodel, 使其implementationFlask-Login要求 method:
from flask_login import UserMixin
from app import db, bcrypt
class User(db.Model, UserMixin):
id = db.Column(db.Integer, primary_key=True)
username = db.Column(db.String(20), unique=True, nullable=False)
email = db.Column(db.String(120), unique=True, nullable=False)
password = db.Column(db.String(60), nullable=False)
def set_password(self, password):
self.password = bcrypt.generate_password_hash(password).decode('utf-8')
def check_password(self, password):
return bcrypt.check_password_hash(self.password, password)
3.4 加载userfunction
我们需要creation一个function来根据userID加载user:
from app.models import User
@login_manager.user_loader
def load_user(user_id):
return User.query.get(int(user_id))
3.5 usingFlask-Login
现 in 我们可以usingFlask-Loginproviding functions:
loginfunctions
from flask import render_template, url_for, flash, redirect
from flask_login import login_user
from app import app, db
from app.forms import LoginForm
from app.models import User
@app.route("/login", methods=['GET', 'POST'])
def login():
form = LoginForm()
if form.validate_on_submit():
user = User.query.filter_by(email=form.email.data).first()
if user and user.check_password(form.password.data):
login_user(user, remember=form.remember.data)
next_page = request.args.get('next')
return redirect(next_page) if next_page else redirect(url_for('home'))
else:
flash('login失败, 请check邮箱 and password', 'danger')
return render_template('login.html', title='login', form=form)
登出functions
from flask_login import logout_user
@app.route("/logout")
def logout():
logout_user()
flash('已成功登出', 'success')
return redirect(url_for('home'))
login保护装饰器
from flask_login import login_required
@app.route("/dashboard")
@login_required
def dashboard():
return render_template('dashboard.html')
获取当 before user
from flask_login import current_user
@app.route("/profile")
@login_required
def profile():
return render_template('profile.html', user=current_user)
4. role and permissionmanagement
in 许 many applicationin, 我们需要 for 不同 user分配不同 role, 并根据role控制 for resource 访问. 例such as, management员可以访问所 has resource, 而普通user只能访问部分resource.
4.1 rolemodeldesign
我们可以creation一个Rolemodel来表示userrole, 然 after using many for many relationships将user and role关联起来:
from flask_login import UserMixin
from app import db
# many for many 关联表
roles_users = db.Table('roles_users',
db.Column('user_id', db.Integer, db.ForeignKey('user.id')),
db.Column('role_id', db.Integer, db.ForeignKey('role.id'))
)
class Role(db.Model):
id = db.Column(db.Integer, primary_key=True)
name = db.Column(db.String(20), unique=True, nullable=False)
description = db.Column(db.String(100))
class User(db.Model, UserMixin):
id = db.Column(db.Integer, primary_key=True)
username = db.Column(db.String(20), unique=True, nullable=False)
email = db.Column(db.String(120), unique=True, nullable=False)
password = db.Column(db.String(60), nullable=False)
roles = db.relationship('Role', secondary=roles_users, backref=db.backref('users', lazy='dynamic'))
4.2 role分配
我们可以 in userregister时 for user分配默认role, or 者 in management界面in手动分配role:
# register时分配默认role
@app.route("/register", methods=['GET', 'POST'])
def register():
form = RegistrationForm()
if form.validate_on_submit():
user = User(username=form.username.data, email=form.email.data)
user.set_password(form.password.data)
# 分配默认role
default_role = Role.query.filter_by(name='User').first()
if default_role:
user.roles.append(default_role)
db.session.add(user)
db.session.submitting()
flash('register成功!', 'success')
return redirect(url_for('login'))
return render_template('register.html', title='register', form=form)
4.3 permissioncheck装饰器
我们可以creation一个装饰器来checkuser is 否具 has specificrole:
from functools import wraps
from flask import flash, redirect, url_for
from flask_login import current_user
def role_required(role_name):
def decorator(f):
@wraps(f)
def decorated_function(*args, **kwargs):
# checkuser is 否login且具 has 指定role
if not current_user.is_authenticated:
flash('请先login', 'warning')
return redirect(url_for('login'))
has_role = any(role.name == role_name for role in current_user.roles)
if not has_role:
flash('您没 has permission访问此页面', 'danger')
return redirect(url_for('home'))
return f(*args, **kwargs)
return decorated_function
return decorator
# using装饰器保护routing
@app.route("/admin/dashboard")
@role_required('Admin')
def admin_dashboard():
return render_template('admin/dashboard.html')
4.4 in 模板incheckrole
我们可以 in 模板incheck当 before user is 否具 has specificrole:
<!-- in 模板incheckrole -->
{% if current_user.is_authenticated %}
<p>欢迎, {{ current_user.username }}!</p>
{% if 'Admin' in current_user.roles|map(attribute='name') %} <a href="{{ url_for('admin_dashboard') }}">management员面板</a>
{% endif %}
<a href="{{ url_for('logout') }}">登出</a>
{% else %}
<a href="{{ url_for('login') }}">login</a>
<a href="{{ url_for('register') }}">register</a>
{% endif %}
5. Flask-Securityscale
Flask-Security is a functions完整 authentication and authorizationscale, 它集成了Flask-Login, Flask-Bcrypt, Flask-WTFetc.scale, providing了register, login, passwordreset, rolemanagementetc.完整functions.
5.1 installationFlask-Security
pip install flask-security
5.2 configurationFlask-Security
from flask import Flask
from flask_sqlalchemy import SQLAlchemy
from flask_security import Security, SQLAlchemyUserDatastore, UserMixin, RoleMixin
app = Flask(__name__)
app.config['SECRET_KEY'] = 'your-secret-key'
app.config['SQLALCHEMY_DATABASE_URI'] = 'sqlite:///site.db'
app.config['SECURITY_PASSWORD_HASH'] = 'bcrypt'
app.config['SECURITY_PASSWORD_SALT'] = 'your-salt'
app.config['SECURITY_REGISTERABLE'] = True
app.config['SECURITY_SEND_REGISTER_EMAIL'] = False
db = SQLAlchemy(app)
# 定义model
roles_users = db.Table('roles_users',
db.Column('user_id', db.Integer, db.ForeignKey('user.id')),
db.Column('role_id', db.Integer, db.ForeignKey('role.id'))
)
class Role(db.Model, RoleMixin):
id = db.Column(db.Integer, primary_key=True)
name = db.Column(db.String(80), unique=True)
description = db.Column(db.String(255))
class User(db.Model, UserMixin):
id = db.Column(db.Integer, primary_key=True)
email = db.Column(db.String(255), unique=True)
password = db.Column(db.String(255))
active = db.Column(db.Boolean())
confirmed_at = db.Column(db.DateTime())
roles = db.relationship('Role', secondary=roles_users, backref=db.backref('users', lazy='dynamic'))
# 设置userdatastore
user_datastore = SQLAlchemyUserDatastore(db, User, Role)
security = Security(app, user_datastore)
# creation默认role and user
with app.app_context():
db.create_all()
# creationrole
user_datastore.find_or_create_role(name='Admin', description='Administrator')
user_datastore.find_or_create_role(name='User', description='Regular User')
# creationmanagement员user
if not User.query.filter_by(email='admin@example.com').first():
user_datastore.create_user(
email='admin@example.com',
password='password',
roles=['Admin']
)
db.session.submitting()
5.3 usingFlask-Security
Flask-Securityproviding了一系列默认routing and 视graph, including:
/login- login页面/logout- 登出页面/register- register页面/change-password- modifypassword页面/reset-password- resetpassword页面
我们可以自定义这些视graph and 模板, 也可以usingFlask-Securityproviding 装饰器来保护routing:
from flask_security import login_required, roles_required
# usinglogin保护装饰器
@app.route("/profile")
@login_required
def profile():
return render_template('profile.html')
# usingrole保护装饰器
@app.route("/admin")
@roles_required('Admin')
def admin():
return render_template('admin.html')
6. best practices
- 始终usingpassword哈希storepassword, 不要store明文password
- using强随机keyserving asSECRET_KEY
- for 不同 environmentusing不同 configuration (Developmentenvironment, testenvironment, produceenvironment)
- usingHTTPSprotocol保护userdata传输
- implementationpasswordresetfunctions
- for login失败次数设置限制, 防止暴力破解
- usingrolepermissionsystem控制resource访问
- 定期updatepassword哈希algorithms and parameter
summarized
本章节介绍了Flaskin authentication and authorizationfunctions, including:
- usingFlask-Bcryptforpassword哈希 and verification
- usingFlask in 置sessionmanagementuserloginstatus
- usingFlask-Loginscaleimplementation更完整 authenticationfunctions
- implementationrole and permissionmanagementsystem
- usingFlask-Securityscaleimplementation完整 authenticationauthorizationfunctions
authentication and authorization is Webapplicationsecurity important 组成部分, Master这些knowledge将helping你Development出security reliable Flaskapplication.