Blog 项目源码:https://github.com/JmilkFan/JmilkFan-s-Blog

目录

  • 目录
  • 前文列表
  • 扩展阅读
  • 用户登录帐号
  • 用户登录状态
  • Flask-Login
  • 使用 Flask-Login 来保护应用安全
  • 小结

前文列表

用 Flask 来写个轻博客 (1) — 创建项目
用 Flask 来写个轻博客 (2) — Hello World!
用 Flask 来写个轻博客 (3) — (M)VC_连接 MySQL 和 SQLAlchemy
用 Flask 来写个轻博客 (4) — (M)VC_创建数据模型和表
用 Flask 来写个轻博客 (5) — (M)VC_SQLAlchemy 的 CRUD 详解
用 Flask 来写个轻博客 (6) — (M)VC_models 的关系(one to many)
用 Flask 来写个轻博客 (7) — (M)VC_models 的关系(many to many)
用 Flask 来写个轻博客 (8) — (M)VC_Alembic 管理数据库结构的升级和降级
用 Flask 来写个轻博客 (9) — M(V)C_Jinja 语法基础快速概览
用 Flask 来写个轻博客 (10) — M(V)C_Jinja 常用过滤器与 Flask 特殊变量及方法
用 Flask 来写个轻博客 (11) — M(V)C_创建视图函数
用 Flask 来写个轻博客 (12) — M(V)C_编写和继承 Jinja 模板
用 Flask 来写个轻博客 (13) — M(V)C_WTForms 服务端表单检验
用 Flask 来写个轻博客 (14) — M(V)C_实现项目首页的模板
用 Flask 来写个轻博客 (15) — M(V)C_实现博文页面评论表单
用 Flask 来写个轻博客 (16) — MV(C)_Flask Blueprint 蓝图
用 Flask 来写个轻博客 (17) — MV(C)_应用蓝图来重构项目
用 Flask 来写个轻博客 (18) — 使用工厂模式来生成应用对象
用 Flask 来写个轻博客 (19) — 以 Bcrypt 密文存储账户信息与实现用户登陆表单
用 Flask 来写个轻博客 (20) — 实现注册表单与应用 reCAPTCHA 来实现验证码
用 Flask 来写个轻博客 (21) — 结合 reCAPTCHA 验证码实现用户注册与登录
用 Flask 来写个轻博客 (22) — 实现博客文章的添加和编辑页面
用 Flask 来写个轻博客 (23) — 应用 OAuth 来实现 Facebook 第三方登录

扩展阅读

用户登录功能
Flask-Login

用户登录帐号

Web上的用户登录功能应该是最基本的功能了,但这个功能可能并没有你所想像的那么简单,这是一个关系到用户安全的功能. 在现代这样速度的计算速度下,用穷举法破解账户的密码会是一件很轻松的事。所以在设计用户口令登录功能的时候需要注意下列几点:

  • 用正则表达式限制用户输入一些非常容易被破解的口令
  • 密文保存用户的口令
  • 不让浏览器记住你的密码
  • 使用 HTTPS 在网上传输你的密码

上述的手段都可以帮助我们的应用程序更好的保护用户的账户信息, 后两点是否实现, 视乎于应用的安全级别是否严格, 我们在这里仅实现前面两点. 除此之外我们还要解决一个非常重要的问题, 就是用户登录状态的处理.

用户登录状态

因为 HTTP 是无状态的协议,也就是说,这个协议是无法记录用户访问状态的,所以用户的每次请求都是独立的无关联的. 而我们的网站都是设计成多个页面的,所在页面跳转过程中我们需要知道用户的状态,尤其是用户登录的状态,这样我们在页面跳转后我们才知道用户是否有权限来操作该页面上的一些功能或是查看一些数据。

所以,我们每个页面都需要对用户的身份进行认证。在这样的应用场景下, 保存用户的登录状态的功能就显得非常重要了. 为了实现这一功能:

  • 第一种方法, 用得最多的技术就是 session 和 cookie,我们会把用户登录的信息存放在客户端的 cookie 里,这样,我们每个页面都从这个 cookie 里获得用户是否已经登录的信息,从而达到记录状态,验证用户的目的.
  • 第二种方法, 我们这里会使用 Flask-Login 扩展是提供支撑.

NOTE: 两种方法是不能够共存的.

Flask-Login

Flask-Login 为 Flask 提供用户 session 的管理机制。它可以处理 Login、Logout 和 session 等服务。
作用:

  • 将用户的 id 储存在 session 中,方便用于 Login/Logout 等流程。
  • 让你能够约束用户 Login/Logout 的视图
  • 提供 remember me 功能
  • 保护 cookies 不被篡改

使用 Flask-Login 来保护应用安全

  • 安装
pip install flask-login
pip freeze . requirements.txt
  • 初始化 LoginManager 对象
    vim extensions.py
from flask.ext.login import LoginManager# Create the Flask-Login's instance
login_manager = LoginManager()
  • 设置 LoginManager 对象的参数
    vim extensions.py
# Setup the configuration for login manager.
#     1. Set the login page.
#     2. Set the more stronger auth-protection.
#     3. Show the information when you are logging.
#     4. Set the Login Messages type as `information`.
login_manager.login_view = "main.login"
login_manager.session_protection = "strong"
login_manager.login_message = "Please login to access this page."
login_manager.login_message_category = "info"@login_manager.user_loader
def load_user(user_id):"""Load the user's info."""from models import Userreturn User.query.filter_by(id=user_id).first()

NOTE 1: login_view 指定了登录页面的视图函数
NOTE 2: session_protection 能够更好的防止恶意用户篡改 cookies, 当发现 cookies 被篡改时, 该用户的 session 对象会被立即删除, 导致强制重新登录.
NOTE 3: login_message 指定了提供用户登录的文案
NOTE 4: login_category 指定了登录信息的类别为 info
NOTE 5: 我们需要定义一个 LoginManager.user_loader 回调函数,它的作用是在用户登录并调用 login_user() 的时候, 根据 user_id 找到对应的 user, 如果没有找到,返回None, 此时的 user_id 将会自动从 session 中移除, 若能找到 user ,则 user_id 会被继续保存.

  • 修改 User models, 以更好支持 Flask-Login 的用户状态检验
class User(db.Model):"""Represents Proected users."""# Set the name for table__tablename__ = 'users'id = db.Column(db.String(45), primary_key=True)username = db.Column(db.String(255))password = db.Column(db.String(255))# one to many: User ==> Post # Establish contact with Post's ForeignKey: user_idposts = db.relationship('Post',backref='users',lazy='dynamic')roles = db.relationship('Role',secondary=users_roles,backref=db.backref('users', lazy='dynamic'))def __init__(self, id, username, password):self.id = idself.username = usernameself.password = self.set_password(password)# Setup the default-role for user.default = Role.query.filter_by(name="default").one()self.roles.append(default)def __repr__(self):"""Define the string format for instance of User."""return "<Model User `{}`>".format(self.username)def set_password(self, password):"""Convert the password to cryptograph via flask-bcrypt"""return bcrypt.generate_password_hash(password)def check_password(self, password):return bcrypt.check_password_hash(self.password, password)def is_authenticated(self):"""Check the user whether logged in."""# Check the User's instance whether Class AnonymousUserMixin's instance.if isinstance(self, AnonymousUserMixin):return Falseelse:return Truedef is_active():"""Check the user whether pass the activation process."""return Truedef is_anonymous(self):"""Check the user's login status whether is anonymous."""if isinstance(self, AnonymousUserMixin):return Trueelse:return Falsedef get_id(self):"""Get the user's uuid from database."""return unicode(self.id)

NOTE 1: is_authenticated() 检验 User 的实例化对象是否登录了.
NOTE 2: is_active() 检验用户是否通过某些验证
NOTE 3: is_anonymous() 检验用户是否为匿名用户
NOTE 4: get_id() 返回 User 实例化对象的唯一标识 id

在完成这些准备之后, 我们可以将 Flask-Login 应用到 Login/Logout 的功能模块中.

  • 在 LoginForm 加入 Remember Me 可选框
class LoginForm(Form):"""Login Form"""username = StringField('Usermame', [DataRequired(), Length(max=255)])password = PasswordField('Password', [DataRequired()])remember = BooleanField("Remember Me")def validate(self):"""Validator for check the account information."""check_validata = super(LoginForm, self).validate()# If validator no passif not check_validata:return False# Check the user whether exist.user = User.query.filter_by(username=self.username.data).first()if not user:self.username.errors.append('Invalid username or password.')return False# Check the password whether right.if not user.check_password(self.password.data):self.password.errors.append('Invalid username or password.')return Falsereturn True
  • jmilkfansblog.controllers.main.py
@main_blueprint.route('/login', methods=['GET', 'POST'])
@openid.loginhandler
def login():"""View function for login.Flask-OpenID will be receive the Authentication-informationfrom relay party."""# Create the object for LoginFormform = LoginForm()# Create the object for OpenIDFormopenid_form = OpenIDForm()# Send the request for login to relay party(URL).if openid_form.validate_on_submit():return openid.trg_login(openid_form.openid_url.data,ask_for=['nickname', 'email'],ask_for_optional=['fullname'])# Try to login the relay party failed.openid_errors = openid.fetch_error()if openid_errors:flash(openid_errors, category="danger")# Will be check the account whether rigjt.if form.validate_on_submit():# Using session to check the user's login status# Add the user's name to cookie.# session['username'] = form.username.datauser = User.query.filter_by(username=form.username.data).one()# Using the Flask-Login to processing and check the login status for user# Remember the user's login status. login_user(user, remember=form.remember.data)identity_changed.send(current_app._get_current_object(),identity=Identity(user.id))flash("You have been logged in.", category="success")return redirect(url_for('blog.home'))return render_template('login.html',form=form,openid_form=openid_form)

NOTE 1: login_user() 能够将已登录并通过 load_user() 的用户对应的 User 对象, 保存在 session 中, 所以该用户在访问不同的页面的时候不需要重复登录.
NOTE 2: 如果希望应用记住用户的登录状态, 只需要为 login_user()的形参 remember 传入 True 实参就可以了.

  • jmilkfansblog.controllers.main.py
@main_blueprint.route('/logout', methods=['GET', 'POST'])
def logout():"""View function for logout."""# Remove the username from the cookie.# session.pop('username', None)# Using the Flask-Login to processing and check the logout status for user.logout_user()identity_changed.send(current_app._get_current_object(),identity=AnonymousIdentity())flash("You have been logged out.", category="success")return redirect(url_for('main.login'))

NOTE 1 Logout 时, 使用 logout_user 来将用户从 session 中删除.

  • jmilkfansblog.controllers.blog.py
    如果我们希望网站的某些页面不能够被匿名用户查看, 并且跳转到登录页面时, 我们可以使用 login_required 装饰器
from flask.ext.login import login_required, current_user@blog_blueprint.route('/new', methods=['GET', 'POST'])
@login_required
def new_post():"""View function for new_port."""form = PostForm()# Ensure the user logged in.# Flask-Login.current_user can be access current user.if not current_user:return redirect(url_for('main.login'))# Will be execute when click the submit in the create a new post page.if form.validate_on_submit():new_post = Post(id=str(uuid4()), title=form.title.data)new_post.text = form.text.datanew_post.publish_date = datetime.now()new_post.users = current_userdb.session.add(new_post)db.session.commit()return redirect(url_for('blog.home'))return render_template('new_post.html',form=form)

引用了这个装饰器之后, 当匿名用户像创建文章时, 就会跳转到登录页面.
NOTE 1: Flask-Login 提供了一个代理对象 current_user 来访问和表示当前登录的对象, 这个对象在视图或模板中都是能够被访问的. 所以我们常在需要判断用户是否为当前用户时使用(EG. 用户登录后希望修改自己创建的文章).

小结

Flask-Login 为我们的应用提供了非常重要的功能, 让应用清楚的知道了当前登录的用户是谁和该用户的登录状态是怎么样的. 这就让我们为不同的用户设定特定的权限和功能提供了可能. 如果没有这个功能的话, 那么所有登录的用户都可以任意的使用应用的资源, 这是非常不合理的.

用 Flask 来写个轻博客 (24) — 使用 Flask-Login 来保护应用安全相关推荐

  1. 用 Flask 来写个轻博客 (37) — 在 Github 上为第一阶段的版本打 Tag

    Blog 项目源码:https://github.com/JmilkFan/JmilkFan-s-Blog 目录 目录 前文列表 第一阶段结语 打 Tag 前文列表 用 Flask 来写个轻博客 (1 ...

  2. 用 Flask 来写个轻博客 (36) — 使用 Flask-RESTful 来构建 RESTful API 之五

    Blog 项目源码:https://github.com/JmilkFan/JmilkFan-s-Blog 目录 目录 前文列表 PUT 请求 DELETE 请求 测试 对一条已经存在的 posts ...

  3. 用 Flask 来写个轻博客 (35) — 使用 Flask-RESTful 来构建 RESTful API 之四

    Blog 项目源码:https://github.com/JmilkFan/JmilkFan-s-Blog 目录 目录 前文列表 POST 请求 身份认证 测试 前文列表 用 Flask 来写个轻博客 ...

  4. 用 Flask 来写个轻博客 (34) — 使用 Flask-RESTful 来构建 RESTful API 之三

    Blog 项目源码:https://github.com/JmilkFan/JmilkFan-s-Blog 目录 目录 前文列表 应用请求中的参数实现 API 分页 测试 前文列表 用 Flask 来 ...

  5. 用 Flask 来写个轻博客 (33) — 使用 Flask-RESTful 来构建 RESTful API 之二

    Blog 项目源码:https://github.com/JmilkFan/JmilkFan-s-Blog 目录 目录 前文列表 扩展阅读 构建 RESTful Flask API 定义资源路由 格式 ...

  6. 用 Flask 来写个轻博客 (32) — 使用 Flask-RESTful 来构建 RESTful API 之一

    Blog 项目源码:https://github.com/JmilkFan/JmilkFan-s-Blog 目录 目录 前文列表 扩展阅读 RESTful API REST 原则 无状态原则 面向资源 ...

  7. 用 Flask 来写个轻博客 (31) — 使用 Flask-Admin 实现 FileSystem 管理

    Blog 项目源码:https://github.com/JmilkFan/JmilkFan-s-Blog 目录 目录 前文列表 扩展阅读 编写 FileSystem Admin 页面 Flask-A ...

  8. 用 Flask 来写个轻博客 (30) — 使用 Flask-Admin 增强文章管理功能

    Blog 项目源码:https://github.com/JmilkFan/JmilkFan-s-Blog 目录 目录 前文列表 扩展阅读 实现文章管理功能 实现效果 前文列表 用 Flask 来写个 ...

  9. 用 Flask 来写个轻博客 (29) — 使用 Flask-Admin 实现后台管理 SQLAlchemy

    Blog 项目源码:https://github.com/JmilkFan/JmilkFan-s-Blog 目录 目录 前文列表 扩展阅读 Flask-Admin BaseView 基础管理页面 Mo ...

最新文章

  1. 深信服:输入一个字符串,帮忙统计字符串里面的每个单词出现的次数,以及非法单词的次数。非法单词的定义为:包含数字(0-9)的单词
  2. 【C语言笔记】关于数组的一个陷阱!
  3. thinking-in-java(14)类型信息
  4. ofdm原理_OFDM技术简介
  5. Octavia API接口慢问题排查引发的思考
  6. python方法的分类(实例方法、类方法、静态方法)
  7. Azure Cosmos DB(Azure 宇宙数据库)--地球已无法阻止微软玩数据库了
  8. 团体程序设计天梯赛-练习集L1-002. 打印沙漏
  9. 《redis设计与实现(第二版)》
  10. 简易nginx load balance示例
  11. 汇川技术小型PLC梯形图编程系列教程(1)小型PLC型号H123U简介
  12. cisco ips原理及console的初始化
  13. Excel如何按照颜色排序
  14. 火狐浏览器批量保存网页图片
  15. PIPI OJ 1203: PIPI发工资(拓扑排序)
  16. 抓饭直播开拓新体育文化,让运动无限精彩
  17. android 控制wifi模块,Android 控制wifi 相关操作实例
  18. uvm 糖果爱好者 subscriber调用parent方法解读
  19. Fomo3D 玩法中文版指南,Dapp 游戏热度超越加密猫CryptoKitties排名第一
  20. python变量值发生变化时输出语句_有一个字符串变量s = 'python program',语句print(s[1:5:2]) 的输出结果是:_学小易找答案...

热门文章

  1. java多线程系列_Java多线程实战系列
  2. 自定义变量 配置文件_跟我一起学.NetCore之自定义配置源热更新对象绑定
  3. javascript object 转换int_关于javascript 中类型转换那些事你知道吗?
  4. java开发人工智能客服_Java开发人工智能客服机器人
  5. 反激qr工作原理_锂电池均衡电路的工作原理
  6. 第八届国际BCI会议(2021 Virtual BCI meeting)
  7. 运用UE和Blocks,仅用两周打造一个VR游戏(52VR完整版译文)
  8. 因为名字叫True,她被苹果iCloud服务器拒绝了
  9. 每天2亿美元投入AI领域,1/10砸向自动驾驶,中国AI论文首超全欧洲 | 斯坦福全球AI年度报告...
  10. 36岁北航校友打造中国自主AI芯片第一股!挖矿起家,6年IPO,市值百亿