为什么说session依赖cookie,以及cookie的常用知识

1,848 阅读8分钟
原文链接: www.cnblogs.com

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传参。

  2. 当我们清空浏览器的时候,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/flask/config.html
  2. https://blog.csdn.net/qq_37526590/article/details/80219227
  3. https://www.cnblogs.com/keyi/p/6823853.html