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

目录

前文列表

用 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 User
return 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_id
posts = 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 = id
self.username = username
self.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 False
else:
return True def is_active():
"""Check the user whether pass the activation process.""" return True def is_anonymous(self):
"""Check the user's login status whether is anonymous.""" if isinstance(self, AnonymousUserMixin):
return True
else:
return False def 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 pass
if 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 False return 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-information
from relay party.
""" # Create the object for LoginForm
form = LoginForm()
# Create the object for OpenIDForm
openid_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.data user = 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.data
new_post.publish_date = datetime.now()
new_post.users = current_user db.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 来写个轻博客

    用 Flask 来写个轻博客 用 Flask 来写个轻博客 (1) — 创建项目 用 Flask 来写个轻博客 (2) — Hello World! 用 Flask 来写个轻博客 (3) — (M)V ...

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

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

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

    目录 目录 前文列表 PUT 请求 DELETE 请求 测试 对一条已经存在的 posts 记录进行 update 操作 删除一条记录 前文列表 用 Flask 来写个轻博客 (1) - 创建项目 用 ...

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

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

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

    目录 目录 前文列表 应用请求中的参数实现 API 分页 测试 前文列表 用 Flask 来写个轻博客 (1) - 创建项目 用 Flask 来写个轻博客 (2) - Hello World! 用 F ...

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

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

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

    目录 目录 前文列表 扩展阅读 RESTful API REST 原则 无状态原则 面向资源 RESTful API 的优势 REST 约束 前文列表 用 Flask 来写个轻博客 (1) - 创建项 ...

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

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

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

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

随机推荐

  1. 爬虫(五)—— 解析库(二)beautiful soup解析库

    目录 解析库--beautiful soup 一.BeautifulSoup简介 二.安装模块 三.Beautiful Soup的基本使用 四.Beautiful Soup查找元素 1.查找文本.属性 ...

  2. luoguP2590 [ZJOI2008]树的统计(树链剖分)

    luogu P2590 [ZJOI2008]树的统计 题目 #include<iostream> #include<cstdlib> #include<cstdio> ...

  3. NGUI的窗体的推动和调节大小(drag object和drag resize object)

    一,我们先添加一个sprite,给sprite添加一个背景图片,然后attach添加一个box Collider,但是这时我们右键attach是找不到drag object的我们需要在add comp ...

  4. k3 cloud中库存转移处理

     有个苗木基地的苗木要转移到另一个,是做那个单据 解决办法:两个基地是同一组织  做直接调拨单就行了 ,不同组织做调拨申请单,然后做 分布式调出  分布式调入

  5. jar 启动脚本

    前段时间用springboot做项目后,每次重新发布都好麻烦, 所以写了个脚本来配合jenkins 发布: #!/bin/bash APP_NAME=application.jar function ...

  6. Linux设置数据库自动备份

    本文为转载,最末端为原地址 以CentOS 7.6系统与Oracle 11g为例: 一.先找到数据库的环境变量 如果是在root账户下,须先登录到数据库所在账户 su oraclecat ~/.bas ...

  7. Java疯狂讲义笔记——Lambda表达式

    Java8新增的Lambda表达式 [特性]支持将代码块作为方法参数,Lambda表达式允许使用更简洁的代码来创建只有一个抽象方法的接口(这种接口被称为函数式接口)的实例. [组成部分]1,形参列表 ...

  8. go语言从例子开始之Example33.工作池

    在这个例子中,我们将看到如何使用 Go 协程和通道实现一个工作池 . Example: package main import "fmt" import "time&qu ...

  9. Linux使用rarcrack暴力破解RAR,ZIP,7Z压缩包

    1.下载http://rarcrack.sourceforge.net/ 2.安装依赖 gcc libxml2-devel  libxslt-devel 3.使用rarcrack your_encry ...

  10. Sass-数据类型

    Sass和JavaScript语言类似,也具有自己的数据类型,在Sass中包含一下几种数据类型 数字:如,1,2,13,10px; 字符串: 有引号字符串或无引号字符串,如,“foo”,"b ...