Falsk框架 Session 與 Flask-Session

  • 2019 年 10 月 10 日
  • 筆記

目錄

Cookie 與 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>

作 者:郭楷豐

出 處:https://www.cnblogs.com/guokaifeng/