Django框架之会话技术之Cookie与Session

开发 架构
会话(Session)跟踪是Web程序中常用的技术,用来跟踪用户的整个会话。常用的会话跟踪技术是Cookie与Session。Cookie通过在客户端记录信息确定用户身份,Session通过在服务器端记录信息确定用户身份。

[[410882]]

会话(Session)跟踪是Web程序中常用的技术,用来跟踪用户的整个会话。常用的会话跟踪技术是Cookie与Session。Cookie通过在客户端记录信息确定用户身份,Session通过在服务器端记录信息确定用户身份。

会话跟踪技术

什么是会话跟踪技术

我们可以把会话理解为客户端与服务器之间的一次会晤,在一次会晤中可能会包含多次请求和响应。

例如,我们给10086打电话,我就是客户端,而10086服务人员就是服务端。从双方接通电话那一刻起,会话就开始了,到某一方挂断电话标识会话结束。在通话过程中,我们会向10086发出多个请求,那么多个请求都在一个会话中。

在Web中,客户使用浏览器向某一服务器发出第一个请求开始,会话就开始了,直到客户关闭了浏览器会话结束。

在一个会话的多个请求中共享数据,这就是会话跟踪技术。

例如,在一个会话中的请求如下:请求银行主页。

1)请求登录(请求参数是用户名和密码);

2)请求转账(请求参数与转账相关的数据);

3)请求信用卡还款(请求参数与还款相关的数据)。

在上面会话中,当前用户信息必须在这个会话中共享的,因为登录的是张三,那么在转账和还款时一定是张三的转账和还款,这就说明我们必须在一个会话过程中共享数据的能力。

会话路径技术使用Cookie或session完成

在Web应用中,HTTP协议是无状态的,即每次请求都是独立的,无法记录前一次请求的状态,每次请求都是一次新的请求。

无状态原因:浏览器与服务器是使用Socket套接字进行通信的,服务器将请求结果返回给浏览器之后,会关闭当前的Socket连接,而且服务器也会在处理页面完毕之后销毁页面对象。

但是,有时候我们需要知道上次请求状态,比如用户是否登录过,浏览过哪些商品等。可以使用会话跟踪,可以使用Cookie、Session、token(自定义的session)。

Cookie

什么叫做Cookie

Cookie,翻译成中文小甜点,小饼干的意思。在HTTP中它表示服务器送给客户端浏览器的小甜点。

Cookie是客户端会话技术,数据都存储在客户端,以key-value进行存储。支持过期时间max_age,默认请求会携带本网站的所有cookie,cookie不能跨域名,不能跨浏览器,cookie默认不支持中文,base64。

Cookie是由服务器创建,通过服务端的响应发送给客户端浏览器的一个键值对,然后浏览器会把Cookie保存起来,并标注出Cookie的来源(哪个服务器的Cookie),当客户端下一次再访问服务器时,默认请求会携带本这个服务器的所有Cookie,这样服务器就可以识别客户端了。

设置cookie应该是服务器response,获取cookie应该在浏览器request,删除cookie应该在服务器response。

Cookie规范

  • Cookie大小上限为4KB;
  • 一个服务器最多在客户端浏览器上保存20个Cookie;
  • 一个浏览器最多保存300个Cookie;

上面的数据只是HTTP的Cookie规范,但是,在浏览器百家争鸣的当下,一些浏览器为了争夺一份份额,展现自己的优势,可能对Cookie的规范扩展,例如每个Cookie的大小为8KB,最多可保存500个Cookie等!但也不会出现把你硬盘占满的可能!

注意,不同浏览器之间是不共享Cookie的。也就是说在你使用IE访问服务器时,服务器会把Cookie发给IE,然后由IE保存起来,当你在使用FireFox访问服务器时,不可能把IE保存的Cookie发送给服务器

Cookie与HTTP头

Cookie是通过HTTP请求头和响应头在客户端和服务器端传递的:

Set-Cookie:响应头,服务器端发送给客户端;

Cookie:请求头,客户端发送给服务器端;格式:Cookie: a=A; b=B; c=C。即多个Cookie用分号隔开;

一个Cookie对象一个Set-Cookie:Set-Cookie: a=A Set-Cookie: b=B Set-Cookie: c=C;

Cookie的覆盖

如果服务器端发送重复的Cookie那么会覆盖原有的Cookie,例如客户端的第一个请求服务器端发送的Cookie是:Set-Cookie: a=A;第二请求服务器端发送的是:Set-Cookie: a=AA,那么客户端只留下一个Cookie,即:a=AA。

请求流程

第一次请求:

① 浏览器第一次请求服务器的时候,不会携带任何cookie信息,,请求头中没有任何cookie信息;

② 当服务器接受到这个请求之后,会做一些验证,例如进行用户名和密码的验证,验证没有问题则可以设置cookie信息。

③ 服务器会为响应设置cookie信息,响应头中有set_cookie信息。

③ 我们的浏览器接收到这个响应之后,发现响应中有cookie信息,浏览器会将cookie信息保存起来。

后续请求:① 第二次或之后的请求都会携带cookie信息,请求头中有cookie信息;

② 服务器接受到请求之后,会发现请求中携带cookie信息,这样就认识是谁的请求了。

代码实现

login.html文件:

<!DOCTYPE html> 
<html lang="en"
<head> 
    <meta charset="UTF-8"
    <title>登录界面</title> 
    <style> 
        #loginbox{ 
            width: 350px; 
            height: 200px; 
            border: 1px solid red; 
            margin: 90px auto; 
            text-align: center; 
        } 
 
    </style> 
</head> 
 
<body> 
    <div id="loginbox"
        <h2>登录界面</h2> 
        <form action="" method="post"
            {% csrf_token %} 
            帐 号:<input type="text" name="username"
            <br/> 
            密 码:<input type="password" name="password"
            <br/> 
            <br/> 
            <input type="submit" value="登录">   
            <input type="button" value="注册"
        </form> 
    </div> 
</body> 
  • 1.
  • 2.
  • 3.
  • 4.
  • 5.
  • 6.
  • 7.
  • 8.
  • 9.
  • 10.
  • 11.
  • 12.
  • 13.
  • 14.
  • 15.
  • 16.
  • 17.
  • 18.
  • 19.
  • 20.
  • 21.
  • 22.
  • 23.
  • 24.
  • 25.
  • 26.
  • 27.
  • 28.
  • 29.
  • 30.
  • 31.
  • 32.

home.html文件:

<!DOCTYPE html> 
<html lang="en"
<head> 
    <meta charset="UTF-8"
    <title>首页</title> 
 
</head> 
<body> 
    欢迎 
    {% if username %} 
        {{ username }} 
    {% else %} 
        游客 
    {% endif %} 
    访问~~ 
    <br> 
    <a href="{% url 'user:logout' %}">退出</a> 
</body> 
</html> 
  • 1.
  • 2.
  • 3.
  • 4.
  • 5.
  • 6.
  • 7.
  • 8.
  • 9.
  • 10.
  • 11.
  • 12.
  • 13.
  • 14.
  • 15.
  • 16.
  • 17.
  • 18.
  • 19.

views.py:

def login(request): 
    if request.method == 'GET'
        return render(request, 'login.html'
 
    elif request.method == 'POST'
        username = request.POST.get('username'
        password = request.POST.get('password'
 
        if username and password:   # 此处为了方便测试,没有去数据库进行校验,实际的需要去数据库校验用户名和密码之类的 
            ''
                响应体: 
                return HttpResponse() 
                return render() 
                return redirect()     
               都可以            
             
            ''
            response = redirect(reverse('user:home')) 
 
            # 设置cookie信息 
            response.set_cookie('username', username) 
            is_login = True 
            response.set_cookie('is_login', is_login) 
 
            return response 
 
        return render(request, 'login.html'
 
def home(request): 
    # 获取cookie信息 
    is_login = request.COOKIES.get('is_login'
    username = request.COOKIES.get('username'
 
    if is_login: 
        return render(request, 'home.html', {'username': username}) 
 
    return redirect(reverse('user:login')) 
 
def logout(request): 
    response = redirect(reverse('user:login')) 
    # 删除cookie信息 
    response.delete_cookie('is_login'
    response.delete_cookie('username')  # 注销,最好都删除,避免注册之后下次还会携带未删除的信息 
 
    return response 
  • 1.
  • 2.
  • 3.
  • 4.
  • 5.
  • 6.
  • 7.
  • 8.
  • 9.
  • 10.
  • 11.
  • 12.
  • 13.
  • 14.
  • 15.
  • 16.
  • 17.
  • 18.
  • 19.
  • 20.
  • 21.
  • 22.
  • 23.
  • 24.
  • 25.
  • 26.
  • 27.
  • 28.
  • 29.
  • 30.
  • 31.
  • 32.
  • 33.
  • 34.
  • 35.
  • 36.
  • 37.
  • 38.
  • 39.
  • 40.
  • 41.
  • 42.
  • 43.
  • 44.
  • 45.

我们可以看到第一次打开登录页面的时候,不携带Cookie信息(csrf暂忽略):

我们再登录,即提交表单之后,服务器设置了Cookie(可参考上面设置Cookie信息代码):

退出,即删除Cookie信息,这里只删除is_login的信息

上面是以明文的方式显示,我们可以进行加盐设置,以加密形式显示:

def login(request): 
    if request.method == 'GET'
        return render(request, 'login.html'
 
    elif request.method == 'POST'
        username = request.POST.get('username'
        response = redirect(reverse('user:home')) 
        #response.set_cookie('username', username) 
        #set_signed_cookie(key,value,salt='加密盐',...)  
        response.set_signed_cookie('username', username, 'qmpython'
 
        return response 
 
 
def home(request): 
    username = request.COOKIES.get('username'
    # 解密 
    #username = request.get_signed_cookie('username''qmpython')     
    response = HttpResponse(f'{username}登录成功'
    return response 
  • 1.
  • 2.
  • 3.
  • 4.
  • 5.
  • 6.
  • 7.
  • 8.
  • 9.
  • 10.
  • 11.
  • 12.
  • 13.
  • 14.
  • 15.
  • 16.
  • 17.
  • 18.
  • 19.
  • 20.

Cookie:具体一个浏览器针对一个服务器存储key-value。

注意,不同浏览器之间是不共享Cookie的。也就是说在你使用IE访问服务器时,服务器会把Cookie发给IE,然后由IE保存起来,当你在使用Chrome访问服务器时,不可能把IE保存的Cookie发送给服务器。

同一个浏览器访问A网站,又去访问B网站,不可能将A的cookie信息携带发送给B服务器。

默认情况下,Cookie只在浏览器的内存中存活,也就是说,当你关闭浏览器后,Cookie就会消失!

问题一:如果只是关闭网站(关闭标签页),没有退出浏览器呢?

A:并不会,如果在不关闭浏览器,只关闭页面时,清除cookie。可以使用js事件处理:

<script> 
window.onunload = function(){ 
    //窗口关闭 
    //在这发一个ajax请求,后台清除cookie 

</script> 
  • 1.
  • 2.
  • 3.
  • 4.
  • 5.
  • 6.

set_cookie参数:

class HttpResponseBase: 
    def set_cookie(self,  
                   key,  # 键 
                   value='', # 值 
                   max_age=None, # cookie有效时长,单位为秒,默认为None表示关闭浏览器失效,指定          为有效数值100表示100秒后自动失效 
                    
                   expires=None, # 支持一个datetime或timedelta,可以指定一个具体的日期,           expires=timedelta(days=10)表示十天后过期。 
                   # max-age和exepires两个指定一个。 
                    
                   path='/',  # Cookie生效的路径,浏览器只会把cookie回传给带有该路径的页面,这样可以避免将;cookie传给站点中的其他的应用。;/ 表示根路径,特殊的:根路径的cookie可以被任何url的页面访问 
                    
                   domain=None,  ''' Cookie生效的域名;你可用这个参数来构造一个跨站cookie。 
                     如, domain=".example.com" 
                     所构造的cookie对下面这些站点都是可读的: 
                     www.example.com 、 www2.example.com  
                和an.other.sub.domain.example.com 。 
                     如果该参数设置为 None ,cookie只能由设置它的站点读取。 
                   ''
                    
                   secure=False,  # 如果设置为True,浏览器将通过HTTPS来回传cookie 
                   httponly=False, # 只能http协议传输,无法被js获取(不是绝对的,底层抓包可以获取到也可以被覆盖) 
                   samesite=None 
                  ):pass 
  • 1.
  • 2.
  • 3.
  • 4.
  • 5.
  • 6.
  • 7.
  • 8.
  • 9.
  • 10.
  • 11.
  • 12.
  • 13.
  • 14.
  • 15.
  • 16.
  • 17.
  • 18.
  • 19.
  • 20.
  • 21.
  • 22.
  • 23.

练习:使用cookie实现上次登录时间

def home(request): 
    # 获取cookie信息 
    username = request.COOKIES.get('username'
    last_login_time = request.COOKIES.get('last_login_time'''
    response = render(request, 'home.html', {'username': username, 'last_login_time': last_login_time}) 
 
    from datetime import datetime 
    now = datetime.now().strftime('%Y-%m-%d %H:%M:%S'
    response.set_cookie('last_login_time', now) 
 
    return response 
  • 1.
  • 2.
  • 3.
  • 4.
  • 5.
  • 6.
  • 7.
  • 8.
  • 9.
  • 10.
  • 11.

Session

前面介绍了Cookie,为什么还需要Session呢?其实很多情况下,只使用Cookie便能完成大部分需求。但是,只使用Cookie往往是不够的,考虑用户登录信息或一些重要的敏感信息,用Cookie存储的话会带来一些问题,最明显的是由于Cookie会把信息保存到本地,因此信息的安全性可能受到威胁。Session的出现很好地解决的这个问题,Session与Cookie类似。

Session是服务器端技术,利用这个技术,服务器在运行时可以为每一个用户的浏览器创建一个其独享的session对象,由于session为用户浏览器独享,所以用户在访问服务器的web资源时,可以把各自的数据放在各自的session中,当用户再去访问该服务器中的其他web资源时,其他WEB资源再从用户各自session中取出数据为用户服务。

Session是服务端会话技术,依赖于Cookie,如果在浏览器中**禁用cookie**的话,那么session就失效了,因为它需要浏览器的cookie值去session里做对比。

1. 启用Session

Django默认启用Session,可以在设置文件settings.py:

MIDDLEWARE = [ 
     
    'django.contrib.sessions.middleware.SessionMiddleware'
 

  • 1.
  • 2.
  • 3.
  • 4.
  • 5.

如果禁用session,则注释即可。

2. 存储方式

在settings.py文件中,可以设置session数据的存储方式,可以保存在数据库、本地缓存等。

2.1 数据库

默认是存储在数据库中的。

SESSION_ENGINE='django.contrib.sessions.backends.db' 
  • 1.

如果存储在数据库中,则需要安装Session应用,默认已设置:

INSTALLED_APPS = [ 
 
    'django.contrib.sessions'

  • 1.
  • 2.
  • 3.
  • 4.

在进行数据库迁移的时候,Django自动帮我们生成了django_session表,有3个字段,分别为session_key,session_data,expris_date,Django中session的默认过期时间是14天。

迁移数据库后,就会生成django_session表:

3. 请求流程

第一次请求:

① 我们第一次请求的时候可能会携带一些信息(例如用户名/密码),cookie中没有任何信息。

② 当服务器接受到这个请求之后,会做一些验证,例如进行用户名和密码的验证,验证没有问题则可以设置session信息。

③ 在设置session信息的同时(session信息保存在服务器端),服务器会在响应头中设置一个sessionid的cookie信息。

④ 客户端(浏览器)在接收到响应之后,会将cookie信息保存起来(保存sessionid的信息)。

后续请求:

① 第二次或之后的请求都会携带sessionid的cookie信息

② 当服务器接收到这个请求之后,获取到sessionid信息,然后进行验证,验证成功,则可以获取session信息(session信息保存在服务器端)

4. 代码实现

def login(request): 
    if request.method == 'GET'
        return render(request, 'login.html'
 
    elif request.method == 'POST'
        ''
        响应体: 
            return HttpResponse() 
            return render() 
            return redirect()     
           都可以 
        ''
        username = request.POST.get('username'
        password = request.POST.get('password'
 
        if username and password:  # 此处为了方便测试,没有去数据库进行校验,实际的需要去数据库校验用户名和密码之类的 
            # 设置session信息 
            request.session['is_login'] = True 
            request.session['username'] = username 
            ''
            以上设置,实际执行以下操作: 
            1、生成随机字符串,例如:123abc!@# 
            2、response.set_cookie("sessionid", 123abc!@#) 
            3、在django_session表中创建一条记录: 
                session_key         session_data 
                123abc!@#           {"is_login":True"username""admin"}         
             
            ''
 
        return redirect(reverse('user:home')) 
 
     
def home(request): 
    is_login = request.session['is_login'
    username = request.session['username'
    ''
    以上执行以下操作: 
    1)  request.COOKIES.get('sessionid'),获得session_key 
    2) 通过上面得到的session_key,在django_session表中获取对应的session_data 
    3) 从session_data获取is_login值。     
    ''
 
    if is_login: 
        return render(request, 'home.html', {'username': username}) 
 
    return redirect(reverse('user:login')) 
 
def logout(request): 
    response = redirect(reverse('user:login')) 
     
    # 删除session数据,即删除了session_data字段值中对应键和值,但是cookie信息还存在,请求还会携带cookie信息,也就成了脏数据,通过cookie再找session,已经找不到了。 
    #del request.session['is_login']  
     
    # 清除所有session,禁用!!! 
    request.session.clear()      
    # 清除当前的会话数据并删除会话的cookie,session,cookie一起干掉,删除session表中session的整条数据。 
    request.session.flush() 
    ''
    执行以下操作: 
    1) request.COOKIE.get('sessionid'),获得session_key 
    2) django_session表中删除session_key对应的记录 
    3) response.delete_cookie('sessionid'
    ''
 
    return response 
  • 1.
  • 2.
  • 3.
  • 4.
  • 5.
  • 6.
  • 7.
  • 8.
  • 9.
  • 10.
  • 11.
  • 12.
  • 13.
  • 14.
  • 15.
  • 16.
  • 17.
  • 18.
  • 19.
  • 20.
  • 21.
  • 22.
  • 23.
  • 24.
  • 25.
  • 26.
  • 27.
  • 28.
  • 29.
  • 30.
  • 31.
  • 32.
  • 33.
  • 34.
  • 35.
  • 36.
  • 37.
  • 38.
  • 39.
  • 40.
  • 41.
  • 42.
  • 43.
  • 44.
  • 45.
  • 46.
  • 47.
  • 48.
  • 49.
  • 50.
  • 51.
  • 52.
  • 53.
  • 54.
  • 55.
  • 56.
  • 57.
  • 58.
  • 59.
  • 60.
  • 61.
  • 62.
  • 63.
  • 64.
  • 65.

打开登录页面,没有携带任何cookie信息(csrf那个先不管)

发起登录请求,设置session信息,其中会生成随机字符串比如mjx9gsq47ofwmcg2ubxtg28uk6idbq2a,然后以这个sessionid为key,这个字符串为value,设置cookie信息。

并且在django_session插入记录,其中session_key字段的值就是sessionid的值,session_data字段的值就是上面设置的session键值对。

然后跳转到home页,就会携带cookie信息,获取sessionid的值,在数据库django_session表中查找是否有相关记录。

如果退出,则删除session信息

从DJANGO角度来看:

第一次请求:

① 第一次请求,在请求头中没有携带任何cookie信息。

② 我们在设置session的时候,session会做2件事。

第一件:将数据保存在数据库中。

第二件:设置一个cookie信息,这个cookie信息是以sessionid为key,value为xxxx,cookie肯定会以响应的形式在响应头中出现。

第二次以及之后的请求:

③ 都会携带cookie信息,特别是sessionid。

如果换了浏览器,还能获取到session信息吗?

解:不可以,因为session依赖于cookie,换了浏览器,都没有cookie信息了。

 

责任编辑:武晓燕 来源: 今日头条
相关推荐

2021-03-23 10:45:23

CookieSession前端

2013-03-19 09:18:35

Cookie

2019-01-04 15:14:18

2023-10-31 18:52:29

网络框架XDP技术

2016-10-10 13:51:42

2018-02-23 14:13:39

前端Cookie用户登录

2014-01-13 09:00:54

PythonDjango

2009-08-06 16:02:05

无Cookie会话

2017-04-28 08:13:08

大数据框架HDFS

2023-07-05 00:38:21

2011-07-11 15:51:50

cookie

2009-09-24 16:13:05

Hibernate S

2009-07-24 17:04:57

ASP.NET中Coo

2009-08-06 16:11:17

2009-07-01 11:05:18

页面与代码分离JSP源码

2009-07-06 16:05:50

JSP特点

2019-11-07 10:37:36

CookieSessionToken

2019-06-11 14:45:25

2021-01-28 09:34:08

解密密钥取证分析

2019-08-29 09:49:50

点赞
收藏

51CTO技术栈公众号