1. 程式人生 > 其它 >Flask的cookie、session

Flask的cookie、session

七、設定cookies

from flask import Flask, make_response

app = Flask(__name__)
app.debug = True


@app.route('/', methods=['POST', 'GET'])
def index():
    # 響應頭,新增make_response
    response = make_response('ok')
    
    # 設定cookies
    response.set_cookie('key', 'val')  
	
    # 刪除cookies
    response.delete_cookie("key")
    
    # 設定響應頭
    response.headers["x-somexx"] = "A SB"
    
    return response


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

7.1 設定cookie的引數

再設定cookie的呼叫set_cookie()時候傳入關鍵字實參 max_age= 值,這個代表多少秒後過期。

注意:max_age引數設定過期時間不相容IE8一下的瀏覽器

...
@app.route('/')
def hello_world():
    resp = Response('設定cookie給瀏覽器')
    resp.set_cookie('user_name', 'mark',max_age=60)
    
    
    # expires = datetime.now()+timedelta(days=30, hours=16)
    # resp.set_cookie('user_name', 'mark', expires=expires)  # 一個月過期時間
    
    return resp
...

引數

key, 鍵
value='', 值
max_age=None, 超時時間 cookie需要延續的時間(以秒為單位)如果引數是\ None`` ,這個cookie會延續到瀏覽器關閉為止

expires=None, 超時時間(IE requires expires, so set it if hasn't been already.)
                   
path='/', Cookie生效的路徑,/ 表示根路徑,特殊的:根路徑的cookie可以被任何url的頁面訪問,瀏覽器只會把cookie回傳給帶有該路徑的頁面,這樣可以避免將cookie傳給站點中的其他的應用。
                   
domain=None, Cookie生效的域名 你可用這個引數來構造一個跨站cookie。如, domain=".example.com"所構造的cookie對下面這些站點都是可讀的:www.example.com 、 www2.example.com 和an.other.sub.domain.example.com 。如果該引數設定為 None ,cookie只能由設定它的站點讀取
                   
secure=False, 瀏覽器將通過HTTPS來回傳cookie
                   
httponly=False 只能http協議傳輸,無法被JavaScript獲取(不是絕對,底層抓包可以獲取到也可以被覆蓋)

7.2 查詢cookie

查詢cookie 是通過請求物件的cookies屬性讀取,讀取的過程是使用設定cookie時的key來讀取到設定cookievalue

...
@app.route('/get_cookie/')
def get_cookie():
    user_name = request.cookies.get('user_name')
    if user_name == 'mark':
        return '{}的資訊'.format(user_name)

    return 'cookie驗證失敗'
...

八、flask的session

session的基本概念:session又稱之為安全的cookie,session是一個思路、是一個概念、一個伺服器儲存授權資訊的解決方案,不同的伺服器,不同的框架,不同的語言有不同的實現,session的目的和cookie完全一致,cookie在客戶端和服務端處理的非常粗糙,cookie在瀏覽器儲存的時候以及傳輸的過程均使用明文,導致了很多安全隱患問題,session的出現就是為了解決cookie儲存資料不安全的問題。

注意:session是一個思路一個概念,session的實現是基於cookie的,session並不像cookie是一項真實存在的技術,可以簡單的理解為把粗糙的cookie在服務端通過加密,永久化等方式提高cookie的安全級別。

實現session的兩種思路

第一種

  1. 客戶端攜帶使用者資訊請求服務端驗證。
  2. 服務端驗證成功後生成隨機的session_id與使用者資訊建立對映後儲存到資料庫中(注意:資料庫可以是任意永久化儲存資料的機制,如redis、memcached、mysql、甚至是檔案等等)。
  3. 服務端把剛剛生成的session_id作為cookie資訊返回給客戶端。
  4. 客戶端收到以session_id為內容的cookie資訊儲存到本地。
  5. 客戶端再次請求的時候會攜帶以session_id為內容的cookie去訪問服務端,服務端取出session_id去資料庫校驗得到使用者資訊。

第二種(flask使用方式)

  1. 客戶端攜帶使用者資訊請求服務端驗證。
  2. 服務端收到使用者資訊驗證成功後,服務端再把使用者資訊經過嚴格的加密加鹽生成session資訊。並且把剛剛生成的session資訊作為cookie的內容返回給客戶端。
  3. 客戶端收到以session資訊為內容的cookie儲存到本地。
  4. 客戶端再次請求的時候會攜帶以session資訊為內容的cookie去訪問服務端,服務端取出session資訊經過解密得到使用者的資訊。

注意:flask使用的就是第二種思路,利用加密解密的方式實現session,實現安全的cookie,服務端並不會做永久化的儲存。

cookie:存放在客戶端的鍵值對
session:存放在客戶端的鍵值對
token:存放在客戶端,通過演算法來校驗

8.1 設定session(使用版)

Flask提供了session物件用來將cookie加密儲存,session通過祕鑰對資料進行簽名以加密資料。

from flask import Flask, session
import os

app = Flask(__name__)
app.config['SECRET_KEY'] = os.urandom(24) # 配置session使用的祕鑰

@app.route('/')
def set_session_info():
    session['username'] = 'mark' # 使用使用者資訊配置sesion資訊作為cookie,並新增到響應體中
    
    return '設定session資訊'

session物件像可以字典一樣操作,內部是把字典的資訊進行加密操作然後新增到相應體中作為cookie,響應的時候會自動返回給瀏覽器。

 session['username'] = 'mark'
 session['userphone'] = '123456'  # 可以指定多條session資訊,統一放到響應的cookie中返回給瀏覽器

8.2 設定session(分析版)

在使用session之前必須現在設定一下金鑰

app.secret_key="asdas" # 值隨便
# app.config['SECRET_KEY'] = os.urandom(24) # 配置session使用的祕鑰
設定:session['username'] = 'xxx'
# 在django中發什麼三件事,1,生成一個隨機的字串 2 往資料庫存 3 寫入cookie返回瀏覽器
# 在flask中他沒有資料庫,但session是怎樣實現的?
	# 生成一個金鑰寫入這個cookie,然後下次請求的時候,通過這個cookie解密,然後賦值給session
    # 我們通過app.session_interface來檢視
    
刪除:session.pop('username', None)
from flask import Flask,session

app = Flask(__name__)
# 要用session,必須app配置一個金鑰
app.secret_key  =  "asdasdihasdiuh"

# 設定session的名字,預設配置檔案中為:session
app.config['SESSION_COOKIE_NAME']="python13session" 

# app.session_interface

#app.session_interface實現了兩個方法,一個叫save_session,一個open_session,
# save_session 存session執行,加密設定cookies
# open_session 取session執行,解密大字典,拿到session
@app.route("/",)
def index():
    #如何設定sessoion
    # 1 匯入session
    # 2 給sessoion設定值
    session['name'] = "egon"
    return "ok"

@app.route("/login")
def login():
    print(session["name"])
    return "login"

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

8.3 設定session有效期

後端Flask跟瀏覽器互動預設情況下,session cookie會在使用者關閉瀏覽器時清除。通過將session.permanent屬性設為True可以將session的有效期延長為31天,也可以通過操作app的配置PERMANENT_SESSION_LIFETIME來設定session過期時間。

案例 3.3.2.1:開啟指定session過期時間模式

from flask import Flask, session
import os

app = Flask(__name__)
app.config['SECRET_KEY'] = os.urandom(24)


@app.route('/')
def set_session_info():
    session['username'] = 'mark'
    session['userphone'] = '123456'
    session.permanent = True # 開啟設定有效期,預設為31天后過期
    return 'Hello World!'
...

設定自定義過期時間

# 通過設定PERMANENT_SESSION_LIFETIME指定具體的過期時間

from datetime import timedelta
app.config['PERMANENT_SESSION_LIFETIME'] = timedelta(hours=1) # 設定為1小時候過期

8.4 獲取sessoin

在Flask中獲取設定的session資訊通過session物件獲取,session物件是繼承了字典類,所以獲取的時候是字典的取值方式。其內部會把瀏覽器傳過來的session資訊解密。

@app.route('/get_session/')
def get_session():
    username = session.get('username')
    userphone = session.get('userphone')
    if username or userphone:
        return "{},{}".format(username, userphone)
    return "session為空"

8.5 刪除session

session物件呼叫pop()可以根據具體的session的key清除掉指定的session資訊。

session物件呼叫clear()可以清除此次請求的瀏覽器關於本域名的所有session資訊

@app.route('/del_session/')
def del_session():
    session.pop('username')
    # session.clear()
    return '刪除成功'

8.6 原始碼分析

session原始碼的執行流程

-save_seesion
    -響應的時候,把session中的值加密序列化放大到了cookie中,返回到瀏覽器中
-open_session
    -請求來了,從cookie中取出值,反解,生成session物件,以後再檢視函式中直接用sessoin就可以了。

原始碼分析

# app.session_interface  點進去

class SecureCookieSessionInterface(SessionInterface):
   
    salt = "cookie-session"
   
    digest_method = staticmethod(hashlib.sha1)
  
    key_derivation = "hmac"
   
    serializer = session_json_serializer
    session_class = SecureCookieSession

    def get_signing_serializer(self, app):
        if not app.secret_key:
            return None
        signer_kwargs = dict(
            key_derivation=self.key_derivation, digest_method=self.digest_method
        )
        return URLSafeTimedSerializer(
            app.secret_key,
            salt=self.salt,
            serializer=self.serializer,
            signer_kwargs=signer_kwargs,
        )
    # 取session的時候執行的
    def open_session(self, app, request):
        s = self.get_signing_serializer(app)
        if s is None:
            return None
        ##cookie鍵是SESSION_COOKIE_NAME"=session
        val = request.cookies.get(app.session_cookie_name)

        print("open_session.session_cookie_name,", app.session_cookie_name, )
        if not val:
            return self.session_class()
        max_age = total_seconds(app.permanent_session_lifetime)
        try:
            data = s.loads(val, max_age=max_age)
            print("self.session_class(data)", self.session_class(data) )
            return self.session_class(data)
        except BadSignature:
            return self.session_class()

    #存session的時候執行的
    def save_session(self, app, session, response):
        domain = self.get_cookie_domain(app)
        path = self.get_cookie_path(app)

        # If the session is modified to be empty, remove the cookie.
        # If the session is empty, return without setting the cookie.
        if not session:
            if session.modified:
                response.delete_cookie(
                    app.session_cookie_name, domain=domain, path=path
                )

            return
        # Add a "Vary: Cookie" header if the session was accessed at all.
        if session.accessed:
            response.vary.add("Cookie")

        if not self.should_set_cookie(app, session):
            return
        httponly = self.get_cookie_httponly(app)
        secure = self.get_cookie_secure(app)
        samesite = self.get_cookie_samesite(app)
        expires = self.get_expiration_time(app, session)
        # 把session做了一個加密,把整個session的key--》val,全部加密,的到一個value值,
        #session是一個大字典,
        val = self.get_signing_serializer(app).dumps(dict(session))
        # 他把session加密後得到的val存到cookie裡面了
        #cookie鍵是SESSION_COOKIE_NAME"=session
        print("原始碼中的session",dict(session))
        print("app.session_cookie_name,",app.session_cookie_name,)
        response.set_cookie(
            app.session_cookie_name,
            val,
            expires=expires,
            httponly=httponly,
            domain=domain,
            path=path,
            secure=secure,
            samesite=samesite,
        )