Falsk框架 Session 与 Flask-Session

Cookie:  #存储大小受限,储存在客户端,有安全隐患

Cookie意为“甜饼”,是由W3C组织提出,最早由Netscape社区发展的一种机制。目前Cookie已经成为标准,所有的主流浏览器如IE、
Netscape、Firefox、Opera等都支持Cookie。由于HTTP是一种无状态的协议,服务器单从网络连接上无从知道客户身份。怎么办呢?
就给客户端们颁发一个通行证吧,每人一个,无论谁访问都必须携带自己通行证。这样服务器就能从通行证上确认客户身份了。这就是Cookie的工作原理。

Session: #来完善Cookie

Session是另一种记录客户状态的机制,不同的是Cookie保存在客户端浏览器中,而Session保存在服务器上。客户端浏览器访问服务器的时候,
服务器把客户端信息以某种形式记录在服务器上。这就是Session。客户端浏览器再次访问时只需要从该Session中查找该客户的状态就可以了,
实质上session就是保存在服务器端的键值对。

Falsk 中 Session 的保管机制

交由客户端保管机制  实现过程
1.开启并设置 Session - session["user"] = 123
2.session 序列化字符串
3.通过 secret_key密钥 进行加密
4.通过 Cookie 返回客户端

接收Session
1.通过客户端 Cookie 获取 session
2.通过 secret_key密钥 进行解密
3.反序列化 字典
4.得到 session

优点:不占用服务器资源
缺点:保存在客户端,安全性相对较差

#一般我们都会使用三方组件提供的session模块,来解决flask session的安全问题,如 Flask-Session这个组件就可以帮我们解决问题.	    

相关的配置

Flask 中的 Session
    - from flask import session
    - 使用session的前提是在 application 中加入 secret_key 如:app.secret_key = "!@#$%^&*()"
    

#Session 相关配置  想要配置生效必须在代码中重新配置 重启后生效
"SECRET_KEY": None,  # 通用密钥
# 设置方法 app.secret_key = "!@#$%^&*()"(编码方便)  或  app.config['SECRET_KEY'] = "!@#$%^&*()"(效率高)

 session.pop('username')  #删除session中的username对应的键和值
 session.clear()          #删除所有session

"SESSION_COOKIE_NAME": "session",
# 设置session 在Cookie Session名称 app.session_cookie_name = 'session'

"SESSION_COOKIE_HTTPONLY": True,  
# 是否只在HTTP请求下开启 session
  
    
############################### 其他配置 ############################### 
    
{
    'DEBUG': False, 			  
    # 是否开启Debug模式 开发环境 log级别低 重启代码错误透传
    
    'TESTING': False,              
    # 是否开启测试模式   测试环境 log级别较高	
    
    'PROPAGATE_EXCEPTIONS': None,  
    # 异常传播(是否在控制台打印LOG) 当Debug或者testing开启后,自动为True
    
    'PRESERVE_CONTEXT_ON_EXCEPTION': None,  
    # 一两句话说不清楚,一般不用它
    
    'SECRET_KEY': None,            
    # 开启Session序列化,在启用Session的时候,一定要有它
    
    'PERMANENT_SESSION_LIFETIME': 31,  
    #  Session的生命周期(秒)默认31秒
    
    'USE_X_SENDFILE': False,  
    # 是否弃用 x_sendfile
    
    'LOGGER_NAME': None,  
    # 日志记录器的名称
    
    'LOGGER_HANDLER_POLICY': 'always',
    
    'SERVER_NAME': None,  # 服务访问域名
    
    'APPLICATION_ROOT': None,  # 项目的完整路径
    
    'SESSION_COOKIE_NAME': 'session',  # 在cookies中存放session加密字符串的名字
    
    'SESSION_COOKIE_DOMAIN': None,  # 在哪个域名下会产生session记录在cookies中
    
    'SESSION_COOKIE_PATH': None,  # cookies的路径
    
    'SESSION_COOKIE_HTTPONLY': True,  # 控制 cookie 是否应被设置 httponly 的标志,
    
    'SESSION_COOKIE_SECURE': False,  # 控制 cookie 是否应被设置安全标志
    
    'SESSION_REFRESH_EACH_REQUEST': True,  # 这个标志控制永久会话如何刷新
    
    'MAX_CONTENT_LENGTH': None,  
    # 如果设置为字节数, Flask 会拒绝内容长度大于此值的请求进入,并返回一个 413 状态码
    
    'SEND_FILE_MAX_AGE_DEFAULT': 12,  # hours 默认缓存控制的最大期限
    
    'TRAP_BAD_REQUEST_ERRORS': False,
    # 如果这个值被设置为 True ,Flask不会执行 HTTP 异常的错误处理,而是像对待其它异常一样,
    # 通过异常栈让它冒泡地抛出。这对于需要找出 HTTP 异常源头的可怕调试情形是有用的。
    
    'TRAP_HTTP_EXCEPTIONS': False,
    # Werkzeug 处理请求中的特定数据的内部数据结构会抛出同样也是“错误的请求”异常的特殊的 key errors 。
    # 同样地,为了保持一致,许多操作可以显式地抛出 BadRequest 异常。
    # 因为在调试中,你希望准确地找出异常的原因,这个设置用于在这些情形下调试。
    # 如果这个值被设置为 True ,你只会得到常规的回溯。
    
    'EXPLAIN_TEMPLATE_LOADING': False,
    
    'PREFERRED_URL_SCHEME': 'http',  # 生成URL的时候如果没有可用的 URL 模式话将使用这个值
    
    'JSON_AS_ASCII': True,
    # 默认情况下 Flask 使用 ascii 编码来序列化对象。如果这个值被设置为 False ,
    # Flask不会将其编码为 ASCII,并且按原样输出,返回它的 unicode 字符串。
    # 比如 jsonfiy 会自动地采用 utf-8 来编码它然后才进行传输。
    
    'JSON_SORT_KEYS': True,
    #默认情况下 Flask 按照 JSON 对象的键的顺序来序来序列化它。
    # 这样做是为了确保键的顺序不会受到字典的哈希种子的影响,从而返回的值每次都是一致的,不会造成无用的额外 HTTP 缓存。
    # 你可以通过修改这个配置的值来覆盖默认的操作。但这是不被推荐的做法因为这个默认的行为可能会给你在性能的代价上带来改善。
    
    'JSONIFY_PRETTYPRINT_REGULAR': True,
    'JSONIFY_MIMETYPE': 'application/json',
    'TEMPLATES_AUTO_RELOAD': None,
}    

使用 Flask-Session 三方组件

安装

#flask_session是flask框架实现session功能的一个插件,用来替代flask自带的session实现机制。

#安装 
pip3 install flask-session 

#Flask-Session 支持 session保存到多个地方如:
- redis     #存放在内网使用,不要存放在公网
- memcached
- filesystem
- mongodb
- sqlalchmey

#存放在redis实例
import redis
from flask import Flask, session
from flask_session import Session
  
app = Flask(__name__)
app.debug = True
app.secret_key = 'xxxx'
  
app.config['SESSION_TYPE'] = 'redis' # session类型为redis
app.config['SESSION_PERMANENT'] = False # 如果设置为True,则关闭浏览器session就失效。
app.config['SESSION_USE_SIGNER'] = False # 是否对发送到浏览器上session的cookie值进行加密
app.config['SESSION_KEY_PREFIX'] = 'session:' # 保存到session中的值的前缀
app.config['SESSION_REDIS'] = redis.Redis(host='127.0.0.1', port='6379', password='123123') # 用于连接redis的配置
  
Session(app)
  
@app.route('/index')
def index():
  session['k1'] = 'v1'
  return 'xx'
  
if __name__ == '__main__':
  app.run()

Flask-Session文件目录

################################  代码  ################################
# __init__.py  Session配置代码
from flask import Flask,request,session
from flask_session import Session
from redis import Redis

from app01.views.user import user_bp

def create_app():
    app = Flask(__name__)

    app.config["DEBUG"] = True
    app.config["SESSION_TYPE"] = "redis" 
    app.config["SESSION_REDIS"] = Redis(host="127.0.0.1",port=6379,db=15)

    # app.session_interface = SecureCookieSessionInterface()  # 原生 Session
    
    # 在Config之后
    
    # 三方组件存活的空间
    Session(app)  #完成flask_session 对原生 Session的替换

    # 在蓝图导入之前
    app.register_blueprint(user_bp)
    
    return app


# app01.py 蓝图代码
from flask import Blueprint

# 蓝图是Flask实例,不可被run
user = Blueprint("user",__name__,url_prefix="/user") # Blueprint name 不能再同一个Flask实例中重复

@user.route("/reg")
def reg():
    return "user注册成功"

@user.route("/login")
def login():
    return "user登录成功"

基础练习题

需求

使用以下数据制作学生详情页面
STUDENT_DICT = {
    1: {'name': '钢蛋', 'age': 17, 'gender': '不详'},
    2: {'name': '铁蛋', 'age': 19, 'gender': '男'},
    3: {'name': '丫蛋', 'age': 18, 'gender': '女'},
}

要求:
1.编写登录页面 登录成功跳转到 学生概况页面 
2.学生概况页面 只显示学生的 ID name  详细信息需点击后访问 学生详情页面页面查看
3.学生详情页面 显示这个学生的所有信息ID name age gender
4.基于Session编写登录验证装饰器 未登录状态只能访问 登录页面 登录成功后才可以访问 学生概况页面 与 学生详情页面

参考答案 (python代码)

from flask import Flask, request, render_template, redirect, session

STUDENT_DICT = {
    1: {'name': '钢蛋', 'age': 17, 'gender': '不详'},
    2: {'name': '铁蛋', 'age': 19, 'gender': '男'},
    3: {'name': '丫蛋', 'age': 18, 'gender': '女'},
}

app = Flask(__name__, template_folder='templates')#实例化一个Flask类,指定模板存放位置
app.secret_key = "!@#$%^&*()"  # 密钥(对session进行加密)
app.debug = True  # 开启Debug模式 修改代码自动重启项目


def check(func):  # 装饰器 检测用户是否登录 利用session中的user的值做判断
    def inner(*args, **kwargs):
        if session.get('user') == 'gkf':
            ret = func(*args, **kwargs)
            return ret
        else:
            return redirect('/login')

    return inner


@app.route('/login', methods=['POST', 'GET'], endpoint='login')#指定路由与接受请求方式,设置endpoint
def login():
    if request.method == 'GET':
        return render_template('login.html')  #如果是get请求返回一个login.html

    if request.method == 'POST':              #如果是post请求,把request.form中页面输入的值取出
        username = request.form.get('username').lower()
        password = request.form.get('password')
        if username == 'gkf' and password == '123':#如果账户密码正确,设置 session["user"] = username
            session["user"] = username
            return redirect('/brief_introduction') #重定向到brief_introduction
        else:
            return '账号或密码错误'  #如果账户密码不对,抛出错误提示

#设置endpoint='brief_introduction'让其与其他的视图函数不冲突,才能正常使用装饰器
@app.route('/brief_introduction', methods=('GET',), endpoint='brief_introduction')
@check  #装饰器 必需添加在@app.route()下,不然会导致,使用装饰器导致@app.route()不被识别,报错
def brief_introduction():
    return render_template('brief_introduction.html', st_obj=STUDENT_DICT)#返回brief_introduction.html 并传入STUDENT_DICT字典 调用名字为st_obj


@app.route('/detailed', methods=('GET',), endpoint='detailed')
@check
def detailed():   #取出前端页面返回的url携带的学生编号, 并把它传给模板detailed.html 进行渲染
    s_id = request.args.get('id') #对url携带参数做效验,确保返回的学生编号正确
    if not s_id:
        return "查询错误请重试"
    if not s_id.isdecimal():
        return "查询错误请重试"
    s_id = int(s_id)
    num = STUDENT_DICT.get(s_id,'')
    if not num:
        return "查询错误请重试"
    else:
        return render_template('detailed.html', id=s_id, st_obj=STUDENT_DICT)


if __name__ == '__main__':
    app.run()

参考答案 (html代码)

<!--templates 下 login.html 代码-->
<!DOCTYPE html>
<html lang="zh-CN">
<head>
  <title>登录</title>
</head>
<body>
<h1>用户登录</h1>
<form action="" method="post" enctype="multipart/form-data">
  <p>用户名: <input type="text" name="username"></p>
  <p>密码: <input type="password" name="password"></p>
  <input type="submit" value="登录">
</form>
</body>
</html>

<!--templates 下 brief_introduction.html 代码-->
<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>学生信息简介</title>
</head>
<body>
<h1>学生信息简介</h1>
<table border="1px">
    <thead>
    <tr>
        <th>编号</th>
        <th>姓名</th>
        <th>操作</th>
    </tr>
    </thead>
    <tbody>
    {% for foo in st_obj %}
    <tr>
        <td>{{ foo }}</td>
        <td>{{ st_obj[foo].name }}</td>
        <!--把学生对应的编号添加到detailed的url上-->
        <td><a href="http://127.0.0.1:5000/detailed?id={{ foo }}">点击查看详情<a></td>
    </tr>
    {% endfor %}
    </tbody>
</table>
</body>
</html>

<!--templates 下 detailed.html 代码-->
<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>学生详细信息</title>
</head>
<body>
<h1>学生详细信息</h1>
<table border="1px">
    <thead>
    <tr>
        <th>编号</th>
        <th>姓名</th>
        <th>年龄</th>
        <th>性别</th>
    </tr>
    </thead>
    <tbody>
    <tr>
        <td>{{ id }}</td>
        <td>{{ st_obj[id].name }}</td>
        <td>{{ st_obj[id].age }}</td>
        {% if st_obj[id].gender=='不详' %}<!--判断对不正常的性别,修改显示结果-->
        <td>男</td>
        {% else %}
         <td>{{ st_obj[id].gender }}</td>
        {% endif %}
    </tr>
    </tbody>
</table>
</body>
</html>
作 者:郭楷丰
声援博主:如果您觉得文章对您有帮助,可以点击文章右下角 推荐一下。您的鼓励是博主的最大动力!
自 勉:生活,需要追求;梦想,需要坚持;生命,需要珍惜;但人生的路上,更需要坚强。带着感恩的心启程,学会爱,爱父母,爱自己,爱朋友,爱他人。
posted @ 2019-09-27 09:51  郭楷丰  阅读(712)  评论(0编辑  收藏  举报
Live2D