前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >flask框架中的一些常见问题

flask框架中的一些常见问题

作者头像
小闫同学啊
发布2019-07-18 13:00:21
1.1K0
发布2019-07-18 13:00:21
举报
文章被收录于专栏:小闫笔记小闫笔记

前一段flask框架的一个小项目虽然写完了,但是里面有些知识,或遗忘或用的稀里糊涂.对于其中涉及到的一些知识点掌握的并不是很透彻,因此在写笔记的时候表述的也不是清晰,今天就来一次大盘点,让我们彻底弄懂这些问题.

我们主要按照下面的思路进行一个分析阐述,大家对于以后的学习也可以借鉴这个思路:

先提一个小点---->你项目里面哪些地方用到了这个知识点----->介绍一下它以及它的优缺点----->你是如何使用的---->用到了其中的哪些知识.

MySQL

项目中凡是涉及到要永久保存的内容我们都用到了MySQL数据库,比如用户的信息(客户的资料可是公司最重要的资料,如果不永久保存,丢了财源,那么就等着倒闭吧,哈哈哈),还有新闻、用户的评论、用户的粉丝(就是关注功能实现的信息)、用户收藏的新闻、用户对评论的点赞、还有新闻的分类(用户界面的首页header部分,还有用户发表新闻的时候所选的分类,管理员在后台审核信息和编辑新闻板式的时候都用到了)等等这些都是用到了MySQL数据库。下面我将后台的数据展示给大家,大家对照着就明白有哪些东西了:

既然这么多地方用到了MySQL这个数据库,那么我们就不能不知道它的相关信息了。MySQL是一款关系型数据库(RDBMS),它使用的是SQL(结构化查询语言)语句进行查询。数据库中的数据都是以表格的形式进行存储的,(元组、记录),(字段、属性)和(指的是我们某一字段设置一个取值范围)前面这三个构成了关系表。

它的特点就是跨平台(管你使用的是Mac、Linux还是Windows,它都支持,你只要想使用就能用,这也就使得它的用户群体很广,因为没有限制)、开源(它的源码是公布的,大家都是可以查看的)、免费(这才是最重要的一点,免费才利于推广,普通用户是富豪的毕竟是少数,免费才是我们选择的第一要素,哈哈哈),然后就是应用范围广。我们在操作数据库的时候一般都是远程,或者在机房里面,哪里有图形界面啊?对着终端,难道安装一个office三件套?不现实吧,正是因为它方便,利于我们远程操作,成为了所有人的最爱,哪哪用的都是它。

项目中我们就用到了扩展Flask-SQLAlchemy,它提供了高层的 ORM(对象关系映射) 和底层的原生数据库的操作。它是一个关系型数据库框架,使用的时候,舍弃了一些性能开销的同时,换来的是开发效率的大大提升。我们直接使用对象来操作数据库就好了,它会帮我们翻译成SQL语句去和MySQL交互,我们就不用记忆SQL语句了,想想就开心。具体的操作可以点击链接查看:flask框架(三)

那么我们就看一下在项目中怎么利用flask-SQLAlchemy使用数据库吧:

代码语言:javascript
复制
class User(BaseModel, db.Model):
    """用户"""
    __tablename__ = "info_user"# 指定表的名称

    #参数1:表示整数类型,  参数2:表示主键
    id = db.Column(db.Integer, primary_key=True)  # 用户编号,设置了主键
    nick_name = db.Column(db.String(32), unique=True, nullable=False)  # 用户昵称
    password_hash = db.Column(db.String(128), nullable=False)  # 加密的密码
    mobile = db.Column(db.String(11), unique=True, nullable=False)  # 手机号
    avatar_url = db.Column(db.String(256))  # 用户头像路径
    last_login = db.Column(db.DateTime, default=datetime.now)  # 最后一次登录时间
    is_admin = db.Column(db.Boolean, default=False)
    signature = db.Column(db.String(512))  # 用户签名
    gender = db.Column(  # 订单的状态
        db.Enum(
            "MAN",  # 男
            "WOMAN"  # 女
        ),
        default="MAN")

# 初始化 user 模型,并设置数据
user = User()
user.nick_name = mobile
user.mobile = mobile

# 将上面的模型添加到数据库,并进行提交
db.session.add(user)
db.session.commit()

上面是网站,用户进行注册的时候,我们需要走的一个大体流程,就是:我们初始化一个用户的模型,也就是创建一个对象user,而SQLAlchemy也就让我们可以像给对象设置属性一样给字段里面添加数据,每个字段的内容都进行了填充,最后将这个对象添加到数据库,然后提交即可。比直接使用数据库用SQL语句要方便的多。

Redis

我们在图片验证码短信验证码session的存储这几个地方使用到了redis数据库。

redis数据库是NOSQL,即非关系型数据库,它是没有外键的,从nosql也可以看出来,它是不支持SQL语句的,刚开始学的时候你也许会觉的头疼,毕竟数据库类型不同,就意味着要记忆更多的语法了。但是当你学习后,你会发现它虽然有很多类型,但是我们常用的语法是没有几句的,而且常用类型的也就一种字符串。具体的操作语句可以点击链接:redis操作命令总结

redis是缓存数据库,即将数据存储在缓存中,缓存的读取速度快,能够大大的提高运行效率,但是保存时间有限。但是这不正与我们用到的session非常的契合吗?session与cookie是一对共存的概念,我们的HTTP默认是无响应的,因为使用了socket套接字,每一次请求完毕之后都会关闭,这样就有一个问题,那就是每次都是全新的访问,大大降低了体验。用session和cookie就可以解决这个问题,浏览器在访问的时候服务器会设置一个cookie发给浏览器,这里面存的是用户的一些信息(比如你浏览过我们网站的哪些东西,你喜欢看哪些文章,方便我给你下次推送,省去你查找的麻烦),然后给服务器存一个session,记录的是一些比较敏感的信息,也是用户的信息(比如用户的用户名、余额、等级、验证码这些东西)。有了这些,我们就可以进行状态保持了,你登陆一个账号,下次访问的时候,只要没有过了session的存储时间,你都不用再次输入账号,这样就很方便了。

redis适用于存储使用频繁的数据,这样减少访问数据库的次数,提高运行效率。说到使用频繁,你想到了什么,那肯定是验证的时候啊,比如我们注册的时候,我们将用户名,密码,验证码等信息一提交,立马就会收到是否注册成功的反馈,这样的效果正是redis存储的一个好处体现。在我们处理验证模块的时候,回想一下过程:

用户一打开注册的弹窗,那么就会有一张图片验证码(这个过程虽然快,但是逻辑操作并不简单。前端会计算一个随机的编码UUID,然后将这个编码发送给后端,后端生成一个验证码图片,生成的时候,有三个值,一个图片的编号,一个是这张图片,一个是图片上的验证码。但是图片的编号我们不使用,我们将前端传过来的UUID作为key,将验证码作为value保存在redis中,然后将图片发送给前端,用户就看到了,因为这个过程很快,所以看不出延迟的,但并不代表编写过程简单)

然后用户填写手机号,图片验证码,点击获取手机验证码,后台收到三个参数(手机号,图片验证码,图片验证码的编号UUID),后台校验参数完整后,看手机号格式是否正确,然后利用UUID从redis中取出验证码和用户输入的验证码进行比对,正确的话就将redis中的图片验证码删掉,然后生成一个随机的短信验证码利用第三方SDK向用户发送短信验证码。后台将用户的手机号作为key,短信验证码作为value保存在redis中。

用户收到之后填写短信验证码,然后点击注册,后台收到三个参数(手机号,短信验证码和用户密码)之后校验短信验证码是否过期,因为redis中保存设置了有效期300s,没有过期的话,验证码校验是否输入正确,正确的话就可以将用户名和密码存储到数据库中了,此处就是MySQL了,因为用户的信息我们要永久保存啊,这就是MySQL的特点了。然后我们给session中保存用户的登录状态,并且返回给前端注册结果。

下面我将项目中上面几种情况用到redis的代码挑拣了出来,大家可以回忆一下:

代码语言:javascript
复制
import redis

# 配置redis
redis_store = redis.StrictRedis(host=Config.REDIS_HOST, port=Config.REDIS_PORT)

# 保存当前生成的图片验证码内容
redis_store.setex('ImageCode_' + code_id, constants.IMAGE_CODE_REDIS_EXPIRES, text)

# redis中保存短信验证码内容
redis_store.set("SMS_" + mobile, sms_code, constants.SMS_CODE_REDIS_EXPIRES)

# 保存用户登录状态
session["user_id"] = user.id
session["nick_name"] = user.nick_name
session["mobile"] = user.mobile

请求钩子

我们的项目中,在完善CSRFToken逻辑和拦截普通用户进入管理员页面的时候,用到了请求钩子。

在客户端和服务器交互的过程中,有些准备工作扫尾工作需要处理的时候,为了让每个视图函数避免编写重复的代码,Flask提过了通用设施的功能,这就是请求钩子

请求钩子是通过装饰器的形式实现的,有4种

  1. before_first_request:在处理第一个请求前执行
  2. before_request:在每次请求前执行,在该装饰函数中,一旦return,视图函数不再执行
    1. 接受一个参数:视图函数作出的响应
    2. 在此函数中可以对响应值,在返回之前做最后一步处理,再返回
  3. after_request:如果没有抛出错误,在每次请求后执行
  4. teardown_request:在每次请求后执行
    1. 接受一个参数:用来接收错误信息

但是我们常用的只有2和3两种,在项目中具体的代码展示一下,方便大家进行回忆:

代码语言:javascript
复制
 #使用请求钩子拦截所有的请求,通过的在cookie中设置csrf_token
    @app.after_request
    def after_request(resp):
        #调用系统方法,获取csrf_token
        csrf_token = generate_csrf()

        #将csrf_token设置到cookie中
        resp.set_cookie("csrf_token",csrf_token)

        #返回响应
        return resp


代码语言:javascript
复制
# 使用请求钩子,拦截用户的请求,只有访问了admin_blue,所装饰的视图函数需要拦截
# 1.拦截的是访问了非登录页面
# 2.拦截的是普通的用户
@admin_blue.before_request
def before_request():
    if not request.url.endswith("/admin/login"):
        if not session.get("is_admin"):
            return redirect("/")

CSRF攻击

什么是csrf攻击?

简单来说就是: 你访问了信任网站A,然后A会用保存你的个人信息并返回给你的浏览器一个cookie,然后呢,在cookie的过期时间之内,你去访问了恶意网站B,它给你返回一些恶意请求代码,要求你去访问网站A,而你的浏览器在收到这个恶意请求之后,在你不知情的情况下,会带上保存在本地浏览器的cookie信息去访问网站A,然后网站A误以为是用户本身的操作,导致来自恶意网站C的攻击代码会被执:发邮件,发消息,修改你的密码,购物,转账,偷窥你的个人信息,导致私人信息泄漏和账户财产安全收到威胁

如何解决?

在psot请求时,form表单或ajax里添加csrf_token(实际项目代码里就是如此简单)

解决原理

添加csrf_token值后,web框架会在响应中自动帮我们生成cookie信息,返回给浏览器,同时在前端代码会生成一个csrf_token值,然后当你post提交信息时,web框架会自动比对cookie里和前端form表单或ajax提交上来的csrf_token值,两者一致,说明是当前浏览器发起的正常请求并处理业务逻辑返回响应,那么第三方网站拿到你的cookie值为什么不能验证通过呢?因为他没你前端的那个随机生成的token值啊,他总不能跑到你电脑面前查看你的浏览器前端页面自动随机生成的token值吧

注意:你打开浏览器访问某个url(页面),默认是get请求,也就是说,你只要访问了url,对应的视图函数里只要不是if xx == post的逻辑就会执行,所以你打开页面,他会先生成cookie(token)值,返回给浏览器, 然后你提交表单,或者发ajax请求时,会将浏览器的cookie信息(token值)发送给服务器进行token比对,这个过程相对于你发起了两次请求,第一次是get,第二次才是post,搞清楚这个,你才能明白csrf怎么比对的

优质文章推荐:

公众号使用指南

redis操作命令总结

前端中那些让你头疼的英文单词

Flask框架重点知识总结回顾

项目重点知识点详解

难点理解&面试题问答

本文参与 腾讯云自媒体分享计划,分享自微信公众号。
原始发表:2019-01-08,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 全栈技术精选 微信公众号,前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体分享计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
相关产品与服务
云数据库 Redis
腾讯云数据库 Redis(TencentDB for Redis)是腾讯云打造的兼容 Redis 协议的缓存和存储服务。丰富的数据结构能帮助您完成不同类型的业务场景开发。支持主从热备,提供自动容灾切换、数据备份、故障迁移、实例监控、在线扩容、数据回档等全套的数据库服务。
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档