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

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/...
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章