為什麼說session依賴cookie,以及cookie的常用知識

liu_486發表於2019-02-16

session的用法

session在Flask中通常用做設定某些頁面的許可權,比如某些頁面必須要登入才可以看到,登入的資訊或標誌就放到session中.它的使用過程如下:

  1. 在整個flask工程的啟動檔案中新增app.config[`SECRET_KEY`] = `you never guess`,SECRET_KEY是用來加密session的,本質上是一個加密鹽.
  2. 再在使用的py檔案中新增from functools import wraps ,封裝裝飾器
  3. 在使用的py檔案中新增from flask import session
  4. 然後寫處理函式
  5. 再在邏輯程式碼中寫已經登入的標誌或者是儲存其他關於請求的資訊
  6. 最後在設定限制的檢視函式前新增限制函式的修飾器

一個簡單的例子

# encoding: utf-8

from flask import Flask
from flask import request, session, redirect
from functools import wraps

app = Flask(__name__)

app.config[`SECRET_KEY`] = `you never guess`  # 使用 session 必須要配置這個,加密簽名.


# 登入、註冊認證函式
def authorize(fn):
    @wraps(fn)
    def wrapper(*args, **kwargs):  # 這裡就像過濾器,有了那個修飾器標誌的檢視函式都必須經過這個函式才可以返回請求
        user = session.get(`logged_in`, None)  # 取得登入標誌
        if user:
            return fn(*args, **kwargs)  # 登入了就返回請求
        else:
            return `need login!`  # 否則就轉到註冊的頁面

    return wrapper


@app.route(`/`)
@app.route(`/home`)
def index():
    session["global_name"] = "global_path"
    return session["global_name"] + `home.html`


@app.route(`/find`)
def find():
    print(vars(session))
    return session.get("global_name", "None") + `find.html`


@app.route(`/doc`)
@authorize  # 這個修飾器表示,這個檢視頁面必須登入才可以訪問
def blog():
    print(session[`username`])
    return `blog.html`


# 登入使用者
@app.route(`/login`, methods=[`GET`, `POST`])
def login():
    if request.method == `GET`:
        return "login.html"
    if request.method == `POST`:
        username = request.values.get(`username`)
        password = request.values.get(`password`)
        if username and password:
            session[`logged_in`] = True  # 登入成功
            session[`username`] = username
            session[`password`] = password
            return username + password
        else:
            return "need username and password"


# 登出使用者
@app.route(`/signout`, methods=[`GET`, `POST`])
@authorize
def logout():
    session[`logged_in`] = False  # 變成false 就意味著需要重新登入了
    return redirect(`/home`)


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

幾句curl測試

# 登入介面與返回,可以看出response中set-cookie了
curl -X POST "http://127.0.0.1:5000/login" -d "username=test&password=123"

HTTP/1.0 200 OK
Set-Cookie: session=eyJsb2dnZWRfaW4iOnRydWUsInBhc3N3b3JkIjoiMTIzIiwidXNlcm5hbWUiOiJsZ2oifQ.DoNGjg.4c2Adke_tzqo5MW_BHs95FvY6i4; HttpOnly; Path=/

# 不帶cookie訪問需要登入的介面
curl  "http://127.0.0.1:5000/doc"
need signin!

# 帶上cookie後訪問通過
curl --cookie "session=eyJsb2dnZWRfaW4iOnRydWUsInBhc3N3b3JkIjoiMTIzIiwidXNlcm5hbWUiOiJsZ2oifQ.DoNGjg.4c2Adke_tzqo5MW_BHs95FvY6i4" "http://127.0.0.1:5000/doc"
user/blog.html

# 登出介面也是同上的.
curl  "http://127.0.0.1:5000/logout"
need signin!

curl --cookie "session=eyJsb2dnZWRfaW4iOnRydWUsInBhc3N3b3JkIjoiMTIzIiwidXNlcm5hbWUiOiJsZ2oifQ.DoNGjg.4c2Adke_tzqo5MW_BHs95FvY6i4" "http://127.0.0.1:5000/signout"
<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 3.2 Final//EN">
<title>Redirecting...</title>
<h1>Redirecting...</h1>
<p>You should be redirected automatically to target URL: <a href="/home">/home</a>.  If not click the link.

# 使用session儲存一些其他資訊也是依賴於cookie存在的 
curl  -v "http://127.0.0.1:5000"
global_pathhome.html

HTTP/1.0 200 OK
Set-Cookie: session=eyJnbG9iYWxfbmFtZSI6eyIgYiI6IloyeHZZbUZzWDNCaGRHZz0ifX0.DoNPag.ooEMHsinZlKRQpLF_-S3axsH3jc; HttpOnly; Path=/

# 想要得到設定global_name,不帶cookie是得不到的
curl  "http://127.0.0.1:5000/find"
Nonefind.html

# 帶cookie 
curl --cookie "session=eyJnbG9iYWxfbmFtZSI6eyIgYiI6IloyeHZZbUZzWDNCaGRHZz0ifX0.DoNPag.ooEMHsinZlKRQpLF_-S3axsH3jc" "http://127.0.0.1:5000/find"
global_pathfind.html

# 設定cookie
curl -v  "http://127.0.0.1:5000/set_cookie"
< HTTP/1.0 200 OK
< Set-Cookie: name=test; Expires=Wed, 19-Sep-2018 03:42:31 GMT; Max-Age=200; Path=/
< Set-Cookie: age=18; Path=/
set_cookie* Closing connection 0

# 獲取cookie
curl --cookie "name=test;age=18" "http://127.0.0.1:5000/get_cookie"
name is test,name is 18

總結:

平時使用瀏覽器訪問介面,很難注意到cookie和session的區別與聯絡,使用curl就把區別暴露了出來.
上面的curl互動可以看出 session是會以set-cookie的方式設定到瀏覽器中,之後的訪問都會將cookie帶上請求其他介面,若使用curl而不帶上cookie就會導致錯誤,這也是為什麼說session是依賴cookie存在的.

  1. cookie存在的目的:

    因為http協議屬於無狀態協議,它不跟蹤從一個客戶端到另一個客戶端的請求資訊.也就是說即使第一次和伺服器連線後並且登入成功,第二次請求伺服器依然不知道請求的是哪個使用者.
    所以使用cookie來解決這個問題:第一次登入成功後,伺服器返回cookie給瀏覽器,然後瀏覽器儲存在本地,當使用者傳送第二次請求時,就會自動的把上次請求儲存的cookie資料攜帶給伺服器,伺服器再根據cookie資料判斷是哪個使用者.

  2. session和cookie的作用類似:
    也是用來儲存使用者相關的資訊,儲存在伺服器端.把使用者的資訊經過加密後儲存在session中,然後產生一個唯一的session_id.
  3. cookie和session的結合使用

    一般有兩種儲存方式:

    (1) 儲存在伺服器端:通過cookie儲存一個session_id,然後具體的資料則是儲存在session中.如果使用者已經登入,則伺服器會在cookie中儲存一個session_id,下次再請求時,會把該session_id攜帶上來,伺服器根據session_id在session庫中獲取使用者的session資料,就知道使用者到底是誰了.以及之前儲存的一些狀態資訊,這種專業術語叫做server side session.
    
    (2) 儲存在客戶端:將session資料加密,然後儲存在cookie中.這種專業術語叫做 client side session.flask框架採用的就是這種方式,但是可以替換成其他形式.
    

flask的session機制

把使用者資訊經過加密後放入到session中,然後把session存放到cookie中.下次請求時,從瀏覽器傳送過來的cookie中讀取到session,然後再從session中讀取資料並解密,獲取最終的使用者資料.這樣可以節省伺服器開銷.

cookie的刪除

1) 可以通過在瀏覽器中設定來清除cookie.

(2) 使用Response的set_cookie進行清除

@app.route(`/del_cookie`)  
def del_cookie():  
    response=make_response(`delete cookie`)  
    response.set_cookie(`Name`,``,expires=0)  
    return response 
    
(3)使用Response的 delete_cookie方法.

@app.route(`/del_cookie2`)  
def del_cookie2():  
    response=make_response(`delete cookie2`)  
    response.delete_cookie(`Name`)  
    return response 

cookie的其他屬性

觀察”Set-Cookie: name=test; Expires=Wed, 19-Sep-2018 03:42:31 GMT; Max-Age=200; Path=/” 可以看出其實cookie有很多屬性,如下:

在chrome控制檯中的resources選項卡中可以看到cookie的資訊。

一個域名下面可能存在著很多個cookie物件。

name 欄位為一個cookie的名稱。

value 欄位為一個cookie的值。

domain 欄位為可以訪問此cookie的域名。

非頂級域名,如二級域名或者三級域名,設定的cookie的domain只能為頂級域名或者二級域名或者三級域名本身,不能設定其他二級域名的cookie,否則cookie無法生成。

頂級域名只能設定domain為頂級域名,不能設定為二級域名或者三級域名,否則cookie無法生成。

二級域名能讀取設定了domain為頂級域名或者自身的cookie,不能讀取其他二級域名domain的cookie。所以要想cookie在多個二級域名中共享,需要設定domain為頂級域名,這樣就可以在所有二級域名裡面或者到這個cookie的值了。
頂級域名只能獲取到domain設定為頂級域名的cookie,其他domain設定為二級域名的無法獲取。

path 欄位為可以訪問此cookie的頁面路徑。 比如domain是abc.com,path是/test,那麼只有/test路徑下的頁面可以讀取此cookie。

expires/Max-Age 欄位為此cookie超時時間。若設定其值為一個時間,那麼當到達此時間後,此cookie失效。不設定的話預設值是Session,意思是cookie會和session一起失效。當瀏覽器關閉(不是瀏覽器標籤頁,而是整個瀏覽器) 後,此cookie失效。

Size 欄位 此cookie大小。

http 欄位  cookie的httponly屬性。若此屬性為true,則只有在http請求頭中會帶有此cookie的資訊,而不能通過document.cookie來訪問此cookie。

secure 欄位 設定是否只能通過https來傳遞此條cookie

下面是flask中cookie對應屬性的配置項

SECRET_KEY    金鑰
SESSION_COOKIE_NAME    會話 cookie 的名稱
SESSION_COOKIE_DOMAIN    會話 cookie 的域。如果沒有設定的話, cookie 將會對 SERVER_NAME 所有的子域都有效。
SESSION_COOKIE_PATH    會話 cookie 的路徑。如果沒有設定或者沒有為 `/` 設定,cookie 將會對所有的 APPLICATION_ROOT 有效。
SESSION_COOKIE_HTTPONLY    控制 cookie 是否應被設定 httponly 的標誌, 預設為 True 。
SESSION_COOKIE_SECURE    控制 cookie 是否應被設定安全標誌,預設為 False。

擴充套件

  1. 既然session依賴cookie,要是瀏覽器禁用了cookie改怎麼達到狀態保持的效果呢?

這個時候,就需要用到URL重寫了,既讓伺服器收到的每個請求引數中都帶有sessioinId,也就是從原本的隱式(headers傳參)變為url或body傳參。

  1. 當我們清空瀏覽器的時候,session會消失嗎?

這個問題包含著一些陷阱。因為很多時候當我們清空瀏覽器以後,確實需要重新登入系統才可以操作,所以很多人自然而然認為清空瀏覽器快取(包含cookie)以後session就會消失。
其實這種結論是錯誤的。要知道,session是存在於伺服器的,你清除瀏覽器快取,只是清除了cookie,跟session一點關係都沒有。那麼為什麼我們卻不能訪問網站,而需要重新登入了呢?因為清空了瀏覽器快取,這時候cookie陣列中必定不會有JSESSIONID這個cookie,所以必須得新建一個session,用新的sessionId來給JSESSIONID這個cookie賦值。由於是新建的session,session中必定沒有userId這樣的屬性值,所以判斷結果自然為空,所以需要重新登入。這次賦值以後,下一次再請求該網站的時候,由於cookie陣列中已經有了JSESSIONID這個cookie,並且能通過該JSESSIONID的值找到相應的session,所以就不需要再重新登入了。

參考

  1. http://www.pythondoc.com/flas…
  2. https://blog.csdn.net/qq_3752…
  3. https://www.cnblogs.com/keyi/…

相關文章