flask_cookie_session

http的会话控制

所谓的会话,就是客户端浏览器和服务端网站之间一次完整的交互过程.

会话的生命周期:

​ - 会话的开始:是在用户通过浏览器第一次访问服务端网站开始.

​ - 会话的结束:时在用户通过关闭浏览器以后,与服务端断开.

所谓的会话控制:就是客户端浏览器和服务端网站之间,进行多次http请求响应的时候,服务端可以记录、跟踪和识别用户的信息而已。

会话的意义体现在哪里呢?

首先我们知道http 是一种无状态协议,协议对于交互性场景没有记忆能力。

无状态:指一次用户请求时,服务器无法知道之前这个用户做过什么,每次请求对于服务器端都是一次新的请求。

无状态原因:浏览器与服务器是使用 socket 套接字进行通信的,服务器将请求结果返回给浏览器之后,会关闭当前的 socket 连接。

但是,有时需要保持下来用户浏览的状态,比如用户是否登录过,浏览过哪些商品等

实现状态保持主要有两种类型:

  • 在客户端存储信息使用Cookie
  • 在服务器端存储信息使用Session

Cookie是由服务器端生成,发送给客户端浏览器,浏览器会将Cookie的key/value保存,下次请求同一网站时就发送该Cookie给服务器(前提是浏览器设置为启用cookie)。Cookie的key/value可以由服务器端自己定义。

使用场景: 登录状态, 浏览历史, 网站足迹,购物车 [不登录也可以使用购物车]

相关注意事项:

  • Cookie是存储在浏览器中的一段纯文本信息,建议不要存储敏感信息如密码,因为电脑上的浏览器可能被其它人使用

  • Cookie基于域名安全,不同域名的Cookie是不能互相访问的

    • 如访问baidu.com时向浏览器中写入了Cookie信息,使用同一浏览器访问sogou.com时,无法访问到baidu.com写的Cookie信息
  • 当浏览器请求某网站时,会将本网站下所有Cookie信息提交给服务器,所以在request中可以读取Cookie信息

设置cookie

设置cookie需要通过flask的Response响应对象来进行设置,由响应对象会提供了方法set_cookie给我们可以快速设置cookie信息。

@app.route("/set_cookie")
def set_cookie():
    """设置cookie"""
    response = make_response("ok")
    # response.set_cookie(key="变量名",value="变量值",max_age="有效时间/秒")
    response.set_cookie("username","xiaoming",100)
    """如果cookie没有设置过期时间,则默认过期为会话结束过期"""
    """cookie在客户端中保存时,用一个站点下同变量名的cookie会覆盖"""
    response.set_cookie("age","100")

    return response

获取cookie

@app.route("/get_cookie")
def get_cookie():
    """获取cookie"""
    print(request.cookies)
    print(request.cookies.get("username"))
    print(request.cookies.get("age"))
    """打印效果:
    {'username': 'xiaoming'}
    """
    return ""

删除cookie

@app.route("/del_cookie")
def del_cookie():
    """删除cookie"""
    response = make_response("ok")
    #把对应名称的cookie设置为过期时间,则可以达到删除cookie
    response.set_cookie("username","",0)
    return response

Session

session即安全的cookie,就是把cookie数据进行了加密处理。对于敏感、重要的信息,建议要存储在服务器端,不能存储在浏览器中,如用户名、余额、等级、验证码等信息

session的作用

session在web程序中发挥着很大的作用,其中最重要的功能就是存储用户的认证信息。我们先来看看基于浏览器的用户认证是如何实现的。当我们使用浏览器登录某个社交网站时,会在登录表单中填写用户名和密码,单击登录按钮后,会向服务器发送一个包含认证数据的请求,服务器接收请求后会查找对应的用户名,然后验证密码是否匹配,如果匹配,就在响应报文中设置一个cookie,比如”login_user:bobo”,响应被浏览器接收后,cookie会被保存在浏览器中。当用户再次向这个服务器发送请求时,根据请求携带的cookie字段中的内容,服务器上的程序就可以判断用户的认证状态,并识别出用户。

使用session对象加密cookie数据

上述流程中是存在问题的,问题就是,在浏览器中添加或者修改cookie是很容易的事,仅通过浏览器插件就可以实现。所以,如果直接把认证信息以明文的方式存储在cookie中是非常不安全的,恶意用户可以通过伪造cookie的内容(login_user:bobo)来获得对网站的权限,冒充别人的账号。为了避免这个问题,需要对敏感的cookie内容进行加密。Flask通过了session对象来将cookie数据进行加密存储。

在Flask中,session对象用来加密Cookie。默认情况下,它会把数据存储在一个名为session的cookie里。

设置程序秘钥

session通过密钥对数据进行签名以加密数据,因此,我们得先设置一个密钥。这里的密钥就是一个具有复杂度和随机性的字符串。可以通过flask.secret_key属性或配置变量SECRET_KEY进行设置。

session的有效期默认是会话期,会话结束了,session就废弃了。

如果将来希望session的生命周期延长,可以session.permanent = True,默认为31天。
PERMANENT_SESSION_LIFETIME:session存活的时间周期,默认为31天

设置session

from flask import Flask, make_response, request,session

app = Flask(__name__)

class Config():
    SECRET_KEY = "123456asdadad"
    DEBUG = True

app.config.from_object(Config)

# 查看当前flask默认支持的所有配置项
# print(app.config)
"""
<Config {
 'DEBUG': False,
 'TESTING': False,
 'PROPAGATE_EXCEPTIONS': None,
 'PRESERVE_CONTEXT_ON_EXCEPTION': None,
 'SECRET_KEY': None,
 'PERMANENT_SESSION_LIFETIME': datetime.timedelta(31),
 'USE_X_SENDFILE': False,
 'LOGGER_NAME': '__main__',
 'LOGGER_HANDLER_POLICY': 'always',
 'SERVER_NAME': None,
 'APPLICATION_ROOT': None,
 'SESSION_COOKIE_NAME': 'session',
 'SESSION_COOKIE_DOMAIN': None,
 'SESSION_COOKIE_PATH': None,
 'SESSION_COOKIE_HTTPONLY': True,
 'SESSION_COOKIE_SECURE': False,
 'SESSION_REFRESH_EACH_REQUEST': True,
 'MAX_CONTENT_LENGTH': None,
 'SEND_FILE_MAX_AGE_DEFAULT': datetime.timedelta(0, 43200),
 'TRAP_BAD_REQUEST_ERRORS': False,
 'TRAP_HTTP_EXCEPTIONS': False,
 'EXPLAIN_TEMPLATE_LOADING': False,
 'PREFERRED_URL_SCHEME': 'http',
 'JSON_AS_ASCII': True,
 'JSON_SORT_KEYS': True,
 'JSONIFY_PRETTYPRINT_REGULAR': True,
 'JSONIFY_MIMETYPE': 'application/json',
 'TEMPLATES_AUTO_RELOAD': None
"""
@app.route("/set_session")
def set_session():
    """设置session"""
    """与cookie不同,session支持python基本数据类型作为值"""
    session["username"] = "xiaohuihui"
    session["info"] = {
        "age":11,
        "sex":True,
    }

    return "ok"


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

获取session

@app.route("/get_session")
def get_session():
    """获取session"""
    print( session.get("username") )
    print( session.get("info") )
    return "ok"

删除session

@app.route("/del_session")
def del_session():
    """删除session"""
    try:
        del session["username"]
        # session.clear() # 删除所有
    except:
        pass
    return "ok"
  • 思考:加密的session数据有没有可能被黑客窃取?
    • SESSION_COOKIE_NAME = 'xxx':在浏览器的开发者工具中通过cookie找到的session的名称就变成了xxx。
    • 如果黑客知道了你的secret_key和你的session的密文值后,就可以破解你的session,但是如果session的名称变了,则很难找到session的密文数据。

Session登录校验

  • 基于session实现免密登录
# 基于session实现免密登录
from flask import Flask, session, request
from flask import render_template

app = Flask(import_name=__name__)

# app.secret_key = 'abc123'
class Config(object):
    DEBUG = True
    SECRET_KEY = 'abc123'

app.config.from_object(Config)

@app.route('/index')
def index():
    if session.get('name'):
        return 'ok'
    return render_template('index.html')

@app.route('/login', methods=['POST', 'GET'])
def login():
    name = request.form.get('username')
    pwd = request.form.get('password')
    radio = request.form.get('radio')

    if name == 'gelong' and pwd == '123':
        if radio:
            session['name'] = 'gelong'
            session['password'] = '123'
        return 'ok'
    else:
        if session.get('name') and session.get('password'):
            print(session.get('name'))  # gelong
            return '登录成功'
        return 'error'

if __name__ == '__main__':
    app.run()
  • 将登录验证作用在多个视图函数中(普通写法)
@app.route('/aa')
def aa():
    if session.get('name'):
        return 'my is aa 页面'
    return render_template('index.html')

@app.route('/bb')
def bb():
    if session.get('name'):
        return 'hello, i is bb'
    return render_template('index.html')
#造成代码冗余
  • 装饰器写法
# 使用装饰器写法为每个函数添加验证是否登录
def login_session(func):
    def inner(*args, **kwargs):
        if session.get('name'):
            return func()
        else:
            return render_template('index.html')
    return inner

@app.route('/aa')  
@login_session
def aa():
    return 'my is aa 页面'

@app.route('/bb')
@login_session
def bb():
    return 'hello, i is bb'
#上述代码报错了:AssertionError: View function mapping is overwriting an existing endpoint function: inner表示视图函数名重复了!都叫inner
  • 视图函数名称重复后的处理方式:

    • 在同名视图函数的@app的装饰器中加入endpoint=’xxx‘
    • endpoint是用来反向生成url地址的。endpoint就是给路由函数起一个别名
    • endpoint默认名就是视图函数名
    @app.route('/aa', endpoint='aa')  # 给路由函数起个别名
    @login_session
    def aa():
        return 'my is aa 页面'
    
  • 假设现在需要将装饰器应用到100个视图函数中,则是否需要在100个视图函数中都应用endpoint呢?

    • 使用functools,会获取函数原始的名称
    # 使用functools, 会获取函数原始的名称
    import functools
    
    def login_session(func):
        @functools.wraps(func) 
        def inner(*args, **kwargs):
            if session.get('name'):
                return func()
            else:
                return render_template('index.html')
        return inner
    
    @app.route('/aa')  # 给路由函数起个别名
    @login_session
    def aa():
        return 'my is aa 页面'
    
    @app.route('/bb')
    @login_session
    def bb():
        return 'hello, i is bb'
    
posted @ 2021-02-10 13:54  死里学  阅读(102)  评论(0)    收藏  举报