Django学习笔记(5)——cookie和session

时间:2021-03-01 19:19:04

一,前言

1.1,什么是会话跟踪技术

  在JavaWeb中,客户向某一服务器发出第一个请求开始,会话就开始了,直到客户关闭了浏览器会话结束。在一个会话的多个请求*享数据,这就是会话跟踪技术。

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

  • 请求登录(请求参数是用户名和密码)
  • 请求转账(请求参数与转账相关的数据)
  • 请求信誉卡还款(请求参数与还款相关的数据)

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

  在程序中,会话跟踪是很重要的事情。理论上,一个用户的所有请求都应该属于同一个会话,而另一个用户的所有请求则该属于另一个会话,二者不能混淆。例如,用户A在超市购买的任何商品都应该放在A的购物车内,不论是用户A什么时候购买的,这都是属于同一个会话,不能放在用户B或者用户C的购物车内,这不属于同一个会话。

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

  我们知道HTTP协议是无状态协议,也就是说每个请求都会独立的! 无法记录前一次请求的状态。但是HTTP协议中可以使用Cookie来完成会话跟踪技术!

  在JavaWeb开发中,使用session来完成会话跟踪,session底层依赖Cookie技术。

  • cookie就是一段字符串,保存于本机电脑上
  • session保存于服务器,用来保存用户的会话信息,依赖于Cookie

  cookie是通过在客户端记录信息确定用户身份,Session是通过在服务端记录信息确定用户身份。

 1.3,cookie与session的实现原理

Django学习笔记(5)——cookie和session

  上图很明显的展示了Django的session和cookie的实现原理。服务器会生成两份相同的cookie字符串,一份保存在本地,一份发向请求的浏览器。浏览器将会受到的cookie字符串保存下来,当下次再发请求时,会将信息与这段cookie一同发送到服务器,服务器得到这段cookie会与本地保存的那份判断是否相同,如果相同就表示用户已经登录成功,保存用户登录成功的状态。Django的session保存在数据库中的数据相当于一个大字典,key为cookie的字符串,value仍是一个字典,字典的key和value为用户设置的相关信息,这样就可以方便的存取session里面的信息。

 1.4,HTTP无状态性

  HTTP被设计为“无状态”,每次请求都处于相同的空间中。在一次请求和下一次情况之间没有任何状态保持,我们无法根据请求的任何方面(IP地址,用户代理等)来识别同一人的连续请求,所以HTTP协议不具备保存之前发送过的请求或响应的功能。

  协议对于事务处理没有记忆能力,对同一个URL请求没有上下文关系,每次的请求都是独立的,它的执行情况和结果与之前的请求和之后的请求时无直接关系的。它不会受前面的请求应答情况直接影响,也不会直接影响后面的请求应答情况。服务器中没有保存客户端的状态,客户端必须每次带上自己的状态去请求服务器。

  如果说每一一种机制用来处理无状态,那么就需要花费时间不停的进行身份验证,正是这样,HTTP引入了session和cookie,即保持了HTTP的无状态性,也使得HTTP的应用称为有状态的。

二,Cookie概述

2.1,什么叫 Cookie

  Cookie翻译成中文是小甜点,小饼干的意思。在HTTP中它表示服务器送给客户端浏览器的小甜点。其实Cookie是 key-value 结构,类似于一个python中的字典。随着服务器端的响应发送给客户端浏览器。然后客户端浏览器会把Cookie保存起来,当下一次再访问服务器时把Cookie再发送给服务器。

Django学习笔记(5)——cookie和session

      注意:cookie不能跨浏览器的,cookie中不能存在中文

  Cookie是由服务器创建,然后通过响应发送给客户端的一个键值对。客户端会保存Cookie,并会标注出Cookie的来源(哪个服务器的Cookie)。当客户端向服务器发出请求时会把所有这个服务器Cookie包含在请求中发送给服务器,这样服务器就可以识别客户端!

查看cookie

  我们使用Chrome浏览器,打开开发者工具。

Django学习笔记(5)——cookie和session

 

 2.2,HTTP的Cookie规范

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

  上面的数据只是HTTP的Cookie规范,但是在浏览器大战的今天,一些浏览器为了打败对手,为了展示自己的能力起见,可能对Cookie规范“扩展”了一些,例如每个Cookie的大小为8KB,最多可保存500个Cookie等!但也不会出现将你的硬盘占满的可能!

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

2.3,Cookie的用途

  • 服务器使用Cookie来跟踪客户端状态
  • 保存购物车
  • 显示上次登录名

2.4,Cookie与HTTP头

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

  • Cookie:

    请求头,客户端发送给服务器端;

    格式:Cookie:a=A;b=B;c=C。即多个Cookie用分号离开;

  • Set-Cookie:

    响应头,服务器端发送给客户端。

    一个Cookie对象一个Set-Cookie:

    Set-Cookie:a=A

    Set-Cookie:b = B

    Set-Cookie:c=C

2.5,Cookie的覆盖

  如果服务器端发送重复的Cookie,那么会覆盖原有的Cookie。

  cookies是浏览器为Web服务器存储的一小段信息。每次浏览器从某个服务器请求页面时,它向服务器会送之前收到的cookies,它保存在浏览器下的某个文件夹下。

Django学习笔记(5)——cookie和session

 

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

2.6,Cookie 的生命周期

  cookie不只有name和value,Cookie还有生命,所谓生命就是Cookie在客户端的有效时间,可以通过setMaxAge(int) 来设置Cookie的有效时间。以秒为单位。不设置默认为关闭窗口,Cookie结束。

  • cookie. setMaxAge(-1):cookie的maxAge属性的默认值就是-1,表示只在浏览器内存中存活。一旦关闭浏览器窗口,那么cookie就会消失。
  • cookie.setMaxAge(60*60):表示cookie对象可存活1小时。当生命大于0时,浏览器会把Cookie保存到硬盘上,就算关闭浏览器,就算重启客户端电脑,cookie也会存活1小时;
  • cookie.setMaxAge(0):cookie生命等于0是一个特殊的值,它表示cookie被作废!也就是说,如果原来浏览器已经保存了这个Cookie,那么可以通过Cookie的setMaxAge(0)来删除这个Cookie。无论是在浏览器内存中,还是在客户端硬盘上都会删除这个Cookie。

过期时间

  cookie 可以有过期时间,这样浏览器就知道什么时候可以删除cookie了。如果cookie没有设置过期时间,当用户关闭浏览器的时候,cookie就自动过期了。我们可以改变SESSION_EXPIRE_AT_BROWSER 的设置来控制session框架的这一行为。缺省情况下,SESSION_EXPIRE_AT_BROWSER 设置为False,这样,会话cookie可以在用户浏览器中保持有效达SESSION_COOKIE_AGE 秒(缺省设置是两周,即1209600秒)。如果你不想用户每次打开浏览器都必须重新登录的话,用这个参数来帮助你。如果SESSION_EXPIRE_AT_BROWSER_CLOSE 设置为 True ,当浏览器关闭时,Django会使cookie失效。

    SESSION_COOKIE_AGE :设置cookie在浏览器中存活的时间

  在settings.py中添加:

Django学习笔记(5)——cookie和session

 

2.7,Django中的cookie语法

  cookies是一种数据存储技术,是将一段文本保存在客户端(浏览器)的一种技术,并且可以长时间的保存。

1,设置cookie

rep = HttpResponse(...) 或 rep = render(request, ...) 或 rep = redirect()  
rep.set_cookie(key,value,...)
rep.set_signed_cookie(key,value,salt='加密盐',...)

  普通设置:

obj.set_cookie('tile' , 'name' , expires = value , path = '/')

  加盐设置(普通cookie是明文传输的,可以直接在客户端直接打开,所以需要加盐,解盐之后才能查看):

obj.set_signed_cookie('k' , 'v' , salt = 'name' )

  

2,cookie参数介绍:

            obj.set_cookie('tile', 'james', expires=value ,path='/',
                           domain=None, secure=False, httponly=False)

  参数:

  • 1,max_age = 1 : cookie生效的时间,单位是秒
  • 2,expires:具体过期日期
  • 3,path = '/' :  指定哪个url 可以访问到cookie,‘/’ 表示所有,即 path = '/'
  • 4,domain = None(None代表当前域名):指定哪个域名以及它下面的二级域名(子域名)可以访问这个cookie
  • 5,secure = False :https安全相关
  • 6,httponly = False:限制只能通过HTTP传输,JS无法在传输中获取和修改。
class HttpResponseBase:
    def set_cookie(self, key, #键
        value = '',     #值
      max_age = None, #超长时间cookie需要延续的时间(以秒为单位)如果参数
                        #是\ None,这个cookie会延续到浏览器关闭为止。
      expires = None, #超长时间,expires默认None, cookie失效的实际日期 / 时间。
      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协议传输,无法被JavaScript获取
                        # (不是绝对,底层抓包可以获取到也可以被覆盖)
              ): pass

  

3,获取cookie

  普通获取:

request.COOKIES.get('k')

  加盐获取:

cookies = request.get_signed_cookie('k' , salt = 'name' )

  

4,删除cookie

response.delete_cookie("cookie_key",path="/",domain=name)

  代码:

def  logout(request):
    rep = redirect("/login/")
    #  删除用户浏览器上之前设置的user  cookie的值
    rep.delete_cookie("user")
    return  rep

  

5,尝试给每个视图函数装饰cookie认证功能

from django.shortcuts import render,redirect,HttpResponse
import datetime
from until import mysqlhelper

# Create your views here.

def cookie_auth(func):
    def weaper(request, *args, **kwargs):
        cookies = request.get_signed_cookie('k', salt='james')
        if cookies == 'v':
            return func(request)
        else:
            return HttpResponse("NG")
    return weaper

now = datetime.datetime.utcnow()
delta = datetime.timedelta(seconds=10)

def login(request):
    if request.method == 'GET':
        return render(request, 'login.html')
    else:
        username = request.POST.get('N')
        password = request.POST.get('P')
        if username == 'james' and password == '123':
            obj = redirect('/modal')
            # obj.set_cookie("tile", "james", max_age = 1,, )
            value = now + delta
            obj.set_cookie('tile', 'james', expires=value ,path='/',
                           domain=None, secure=False, httponly=False)
            obj.set_signed_cookie('k', 'v', salt='james', )
            return obj
        else:
            return render(request, 'login.html')
def test(request):
    return render(request, 'layout.html')

@cookie_auth
def modal(request):
    sql = '''
    SELECT  teacher.id as tid,teacher.`name`as tname,class.title FROM day64.teacher 
    LEFT JOIN teacher_class ON day64.teacher.id=day64.teacher_class.tid
    LEFT JOIN day64.class ON day64.teacher_class.cid=day64.class.id;
    '''
    teacher_list = mysqlhelper.get_list(sql, [])
    res = {}
    for row in teacher_list:
        tid = row['tid']
        if tid in res:
            res[tid]['titles'].append(row['title'])
        else:
            res[tid] = {'tid':row["tid"],'tname':row["tname"],'titles':[row["title"],]}

    class_list=mysqlhelper.get_list("SELECT id ,title FROM day64.class" ,[])
    return render(request,'modal.html',{"list":res.values(),"class_list":class_list} )

  

2.8,实例:Cookie版登陆校验

views视图代码:

from django.shortcuts import render, HttpResponse, redirect
import datetime
# Create your views here.

# 登录验证装饰器
def login_auth(func):
    def inner(request, *args, **kwargs):
        # 获取请求的全路径
        url = request.get_full_path()
        # /shopping/?nana=ppp
        # 服务器先拿到cookie,如果拿到则直接跳转到shopping或者order视图页面
        is_login = request.COOKIES.get('is_login')
        # xxx = request.get_signed_cookie('xxx' ,salt='123')
        # 拿到cookie 判断是不是登录状态
        if is_login:
            # 是登录状态直接调用被装饰的函数,返回我们想要的页面
            ret = func(request, *args, **kwargs)
        else:
            # 服务端如果拿不到正确的cookie就会重定向了login这个页面,但是带了一些参数
            return redirect('/login/?next=%s'%url)
        # 当清楚浏览器缓存是直接登录http://127.0.0.1:8000/shopping/,会跳转到登录页面
        # 并且在url后面拼上http://127.0.0.1:8000/login/?next=/shopping/
        # 127.0.0.1:8000/login/?next=/shopping/?nana=ppp
        return ret
    return inner

def login(request):
    if request.method == 'POST':
        # 装饰器中redirect('/login/?next=%s'%url)通过get就可以拿到next的值
        # url = request.GET.get('name')
        username = request.POST.get('username')
        password = request.POST.get('password')
        if username == 'james' and password == '123':
            # 登录成功后跳转到该页面
            # obj = redirect(url)
            obj = HttpResponse("登录成功")
            # 当我们不指定path时,被装饰的视图函数页面只登录后都可以直接被访问,而不需要重新登录
            obj.set_cookie('is_login', True)
            # 浏览器只会把cookie会传给带有该路径的页面
            # 也就说当我们登录成功后,访问shopping页面时可以直接登录
            # obj.set_cookie('is_login', True, path='/shopping/')
            # 但是我们访问order页面时,仍然会跳到登录页面让我们重新登录
            now = datetime.datetime.now().strftime('%Y-%m-%d %X')
            # 设置cookie,在浏览器的请求头中的cookie中我们可以看到我们设置的额cookie
            # 并将登录时间也写到cookie中
            obj.set_cookie('last_time', now)
            # 在浏览器中打开可以看到一个key
            obj.set_cookie('username','james')
            obj.set_cookie('password','123')
            # 获取cookie,并对cookie进行加盐,加盐后的123 是进行加密的遗传字符吗
            obj.set_signed_cookie('james','durant',salt='123')
            # 登录成功后对其进行重定向
            return obj
        else:
            obj = HttpResponse("登录失败")
            return obj
    return render(request, 'login.html')

# 退出登录
def logout(request):
    obj = HttpResponse("注销成功")
    # 登录成功获得服务端发送来的cookie,删除cookie,
    # 这样我么在浏览器中在输入 http://127.0.0.1:8000/shopping/
    # J就会在此跳到登录页面让我门重新登录
    return obj

# 购物页面
def shopping(request):
    return render(request, 'shopping.html', locals())
    # locals()用法:locals()可以直接将函数中所有变量全部传给模板
    # 当然这可能会传递一些多余的参数,有点浪费内存的嫌疑

    # 登录成功后再请求shopping页面,在请求头就可以看到cookie中带有键值对的形式:
    # Cookie: csrftoken = ybQexReWXIWaO0If0p0I7NubfBSvjUlSfR2EWWX8eKCwXbL8lDo4Kk3ar6Nbr5ZR;
    # last_time = "2018-09-13 18:13:17";
    # name = james;
    # pwd = 123;
    # xxx = xxxxxx:1g0Ocf: 1kRB9Q0FVXlVzstrRbm4fJ61eF0

# 订单页面
@login_auth
def order(request):
    return render(request, 'order.html', locals())

  

url路由配置

from django.contrib import admin
from django.conf.urls import url
# 需要先导入对应的app的views文件
from cookieapp import views

urlpatterns = [
    url(r'^admin/', admin.site.urls),
    url(r'^login/', views.login),
    url(r'^order/', views.order),
    url(r'^shopping/',views.shopping),
    url(r'^logout/', views.logout),
]

  

template模板代码

login.html

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>Title</title>
</head>
<body>

    <form action="" method="POST">
        <p>用户名:<input type="text" name="username"></p>
        <p>密码:  <input type="password" name="password"></p>
        <input type="submit">
    </form>


</body>
</html>

  

shopping.html

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>Title</title>
</head>
<body>

    <h1>购物车页面</h1>

</body>
</html>

  

order.html

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>Title</title>
</head>
<body>

    <H1>订单页面</H1>

</body>
</html>

  

结果展示:(登陆页面:127.0.0.1:8000/login.login)

Django学习笔记(5)——cookie和session

登陆成功:

Django学习笔记(5)——cookie和session

cookie展示

Django学习笔记(5)——cookie和session

 Django学习笔记(5)——cookie和session

Django学习笔记(5)——cookie和session

Django学习笔记(5)——cookie和session

 

三,session概述

  session就是在服务器端的“Cookie”,将用户数据保存在服务器端,远比保存在用户端要安全,方便和快捷的多。Session依赖于Cookie,但是与Cookie不同的地方就是在于session将所有的数据都放在服务器端,用户浏览器的cookie中只会保存一个非明文的识别信息,比如哈希值。

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

  Django的Session机器会向请求的浏览器发送cookie字符串。同时也会保存在本地一份,用来验证浏览器登录是否为同一用户。他存在于服务器,Django默认会把session存入数据库中。

  Session依赖于Cookie,如果浏览器不能保存cookie,那么session就失效了。因为他需要浏览器的cookie和session做对比。session就是用来在服务器端保存用户的会话状态。

3.1,Django中session语法

class backends.base.SessionBase
        # 这是所有会话对象的基类,包含标准的字典方法:
        __getitem__(key)
            Example: fav_color = request.session['fav_color']
        __setitem__(key, value)
            Example: request.session['fav_color'] = 'blue'
        __delitem__(key)
            Example: del request.session['fav_color']  # 如果不存在会抛出异常
        __contains__(key)
            Example: 'fav_color' in request.session
        get(key, default=None)
            Example: fav_color = request.session.get('fav_color', 'red')
        pop(key, default=__not_given)
            Example: fav_color = request.session.pop('fav_color', 'blue')

  类似于字典数据类型的内置方法

        keys()
        items()
        setdefault()
        clear()
 
 
        # 它还有下面的方法:
        flush()
            # 删除当前的会话数据和会话cookie。经常用在用户退出后,删除会话。
 
        set_test_cookie()
            # 设置一个测试cookie,用于探测用户浏览器是否支持cookies。由于cookie的
工作机制,你只有在下次用户请求的时候才可以测试。

        test_cookie_worked()
            # 返回True或者False,取决于用户的浏览器是否接受测试cookie。你必须在之
前先调用set_test_cookie()方法。

        delete_test_cookie()
            # 删除测试cookie。

        set_expiry(value)
            # 设置cookie的有效期。可以传递不同类型的参数值:
        • 如果值是一个整数,session将在对应的秒数后失效。例如request.session.
set_expiry(300) 将在300秒后失效.
        • 如果值是一个datetime或者timedelta对象, 会话将在指定的日期失效
        • 如果为0,在用户关闭浏览器后失效
        • 如果为None,则将使用全局会话失效策略
        失效时间从上一次会话被修改的时刻开始计时。
 
        get_expiry_age()
            # 返回多少秒后失效的秒数。对于没有自定义失效时间的会话,这等同于SESSION_COOKIE_AGE.
            # 这个方法接受2个可选的关键字参数
        • modification:会话的最后修改时间(datetime对象)。默认是当前时间。
        •expiry: 会话失效信息,可以是datetime对象,也可以是int或None
 
        get_expiry_date()
            # 和上面的方法类似,只是返回的是日期
 
        get_expire_at_browser_close()
            # 返回True或False,根据用户会话是否是浏览器关闭后就结束。
 
        clear_expired()
            # 删除已经失效的会话数据。
        cycle_key()
            # 创建一个新的会话秘钥用于保持当前的会话数据。django.contrib.auth.login() 
会调用这个方法

  

  • 获取session:request.session[key]
  • 设置session:request.session[key] = value
  • 删除session:del  request[key]
1、设置Sessions值
          request.session['session_name'] ="admin"

2、获取Sessions值
          session_name = request.session["session_name"]

3、删除Sessions值
          del request.session["session_name"]

4、flush()
     删除当前的会话数据并删除会话的Cookie。
     这用于确保前面的会话数据不可以再次被用户的浏览器访问

5、get(key, default=None)
  fav_color = request.session.get('fav_color', 'red')  

6、pop(key)
  fav_color = request.session.pop('fav_color')  

7、keys()

8、items()  

9、setdefault()  

10 用户session的随机字符串

  

用户session的随机字符串举例:

request.session.session_key

# 将所有Session失效日期小于当前日期的数据删除
request.session.clear_expired()

# 检查 用户session的随机字符串 在数据库中是否
request.session.exists("session_key")

# 删除当前用户的所有Session数据
request.session.delete("session_key")

request.session.set_expiry(value)
*如果value是个整数,session会在些秒数后失效。
*如果value是个datatime或timedelta,session就会在这个时间后失效。
*如果value是0, 用户关闭浏览器session就会失效。
*如果value是None, session会依赖全局session失效策略。

  

3.2,session配置

Django默认支持Session,并且默认是将Session数据存储在数据库中,即:django_session 表中。

a. 配置 settings.py

    SESSION_ENGINE = 'django.contrib.sessions.backends.db'   # 引擎(默认)

    SESSION_COOKIE_NAME = "sessionid"                       
# Session的cookie保存在浏览器上时的key,即:sessionid=随机字符串(默认)

    SESSION_COOKIE_PATH = "/"                               
# Session的cookie保存的路径(默认)

    SESSION_COOKIE_DOMAIN = None                            
 # Session的cookie保存的域名(默认)

    SESSION_COOKIE_SECURE = False                            
# 是否Https传输cookie(默认)

    SESSION_COOKIE_HTTPONLY = True                           
# 是否Session的cookie只支持http传输(默认)

    SESSION_COOKIE_AGE = 1209600                             
# Session的cookie失效日期(2周)(默认)

    SESSION_EXPIRE_AT_BROWSER_CLOSE = False                  
# 是否关闭浏览器使得Session过期(默认)

    SESSION_SAVE_EVERY_REQUEST = False                       
# 是否每次请求都保存Session,默认修改之后才保存(默认)

  

3.3,session的作用

  session下次通过cookie中的sessionID(键)获取用户信息值(值)

1,会话保持,记住用户的登录状态(WEB网站,分布式架构)

2,避免了敏感信息保存在客户端,防止客户端修改cookie信息(和cookie的区别)

session的过期时间

  session的过期时间:django默认设置是2周 ,如果session过期,浏览器再携带之前的cookie就不能免登陆了。因为cookie已经失效了。

  前端:如果超出了session的过期时间,那么浏览器会自动将对应的cookie删除掉

  后端:django没有对过期的session做任何处理

如何删除后台保留的一些过期的session信息

python manage.py clearsessions

  当然,如果用户在过期时间内主动退出登录,那么django会将该用户对应的session数据给删除掉  (request.session.flush())

  但是如果用户在登录完以后没有主动退出,并且超出了过期时间,用户需要重新登录,但django中的过期session是不清除的,需要定期清理过期的session数据。

3.4,session ID的作用

  当客户端第一次请求session时,服务器端会为客户端创造一个session对象,并且生成一个session ID (通过加密算法)。然后保存在cookie中,当用户再次登录时,客户端通过cookie,将session ID传到服务器,去和服务器中的session ID进行比对,寻找这个session,然后根据查找结果执行对应的操作。

  因为session ID 是保存在cookie中,而cookie是存在于客户端,所以session ID 并不安全。

  为了避免session ID 被盗,我们可以这样做:

  • 1,敏感操作需要用户输入密码来进行二次认证
  • 2,网站HTTPS化,提高消息传递过程中的安全系数
  • 3,用户使用一个密钥对参数进行hash,这样即使cookie被盗取,也会因为没有密钥而无法获取sessionID。

3.5,session的保存方式

  Session是大多数网站都需要具备的功能,Django为我们提供了一个通用的session框架,并且可以使用多种session数据的保存方式:

  • 保存在数据库内
  • 保存到缓存
  • 保存到文件内
  • 保存到cookie内

  通常情况下,没有特别需求的话,请使用保存在数据库内的方式,尽量不要保存在cookie内。

  django的session框架支持匿名会话,封装了cookies的发送和接收过程。cookie包含一个会话ID而不是数据本身(除非你使用的是基于后端的cookie)。

  django的会话框架完全的,唯一的基于cookie。它不像PHP一样,把会话的ID放在URL中,那样不仅使得URL变得丑陋,而且使得你的网站易于受到通过“Referer”头部进行窃取会话ID的攻击。

3.6,session流程解析

  session的应用要依赖于cookie(session就是cookie的变种)

1,每次用户第一次访问服务端,把用户的唯一字符串session_id加到cookie里面发送给客户端;

  当用户登录之后,生成一个字典{key : value},将字典存入session,key是自动生成的一段字符串表示,返回cookie,value是一个自定义格式的字典。

Django学习笔记(5)——cookie和session

2,服务器端保存随机字符串(sessionID :{用户信息}) 服务端

  当我们在django中用到session时,cookie由服务端随机生成,写到浏览器的cookie中。每个浏览器都有自己的cookie值,是session寻找用户信息的唯一标识。每个浏览器请求到后台接受的request.session 等价于在1中的session字典key (cookie)对应的value。

Django学习笔记(5)——cookie和session

 3,session的好处就是客户端只有cookie的值,但是始终没有用户信息。

  用法:request.session.get('k1')    request.session['k1'] = 'v1'

  session依赖于cookie,cookie保存在浏览器。session保存在服务器 端。

3.7,示例

  注意:这里需要注意在session中,我们可以设置多个key:value的值,方便我们做很多事情,比如判断哪个用户:

  如果用户登录后,那么他肯定有一个cookie,而当他访问购物车的时候,怎么判断是哪个用户呢?我们可以在session设置,当用户登录的时候,我们把用户名增加到session中,那么用户携带cookie访问的时候,我们就能判断是哪个用户来访问的。

比如下面的对应关系:

user1

cookie : 001

  

server session(举例格式)

{session:001 {'IS_LOGIN':'True',username:'james'}}

  

3.7.1,一段简单的Django中实现session的代码,判断用户是否已经成功登陆

from django.shortcuts import render,redirect,HttpResponse

# Create your views here.

def login(request):
    if request.method == 'POST':
        username = request.POST.get('username')
        password = request.POST.get('password')
        if username == 'james' and password == '123':
            # 设置session
            request.session['IS_Login'] = True
            return redirect('/app01/home')
    
    return render(request, 'login.html')

def home(request):
    # 获取session
    is_login = request.session.get("IS_LOGIN", False)
    if is_login:
        return HttpResponse('order')
    else:
        return redirect('/app01/login/')

  

3.7.2 在视图中使用session

def session_test(request):
    # 设置session值,会将其存到db.sqlite数据库中的django_session表中
    # 每发送一次请求就会在django_session表中添加一条记录
    request.session['username'] = 'james'
    request.session['is_login'] = True
    '''
    1,生成一个随机字符串
    2,把随机字符串以cookie的形式写回给浏览器
    3,会在数据库中存{'随机字符串': {'username' : 'james' , 'is_login': True}
    '''
    return HttpResponse("ok")

def get_session(request):
    # 在浏览器中输入路由地址就会访问访问该视图函数
    # 获取session值,但是一定要先获取session值,否则获取session时会报错
    # name=request.session['username']
    # is_login=request.session['is_login']
    # print(name)
    # print(is_login)
    # session取值的两种方式,字典key或者get进行取值
    # request.session['k1']
    # request.session.get('k1', None)
    # 设置session的值
    # request.session['k1'] = 123

    # name=request.session.setdefault('username', 123)
    # 如果session中的username有值就不会设置,无值则进行设置
    # print(name)      #-----123
    # del request.session['k1']
    # 通过key将session的值进行删除
    # print(request.session.session_key)
    # 取出表django_session表中session_key字段对应的值
    # 将所有Session失效日期小于当前日期的数据删除
    # request.session.clear_expired()
    # 判断session表中session_key字段的值是否存在,返回值为布尔值
    # print(request.session.exists("b16mh23xajc2u69vazvivf8ruo4ilumi"))
    # 会删除数据库的session表中的session的记录,但是浏览器中的session id还在
    # request.session.delete()
    # 删除当前的会话数据并删除会话的Cookie。
    # 通常用它,他会把数据库以及浏览器会话中的session id都删除
    # (调用视图函数test可以看到效果的确是已经被删除)

    request.session.flush()
    return HttpResponse("OK")

  flush()方法是比较安全的一种做法,而且一次性将session中的所有内容全部清空,确保不留后患,但是也有不好的地方,那就是如果你在session中夹带了一点“私货”,会被一并删除,这一点一定要注意。

  比如登出视图函数:

def logout(request):
    if not request.session.get('is_login', None):
        # 如果本来就未登录,也就没有登出一说
        return redirect("/index/")
    request.session.flush()
    # 或者使用下面的方法
    # del request.session['is_login']
    # del request.session['user_id']
    # del request.session['user_name']
    return redirect("/index/")

  

3.7.3 在视图函数中使用装饰器装饰CBV

 

from django.utils.decorators import  method_decorator
#  在视图函数中装饰类 需要先导入method_decorator模块
from django.views import View

# 在类上加装饰器需要指定name的值,也就是要指定装饰类内的那个函数
# 当我们登录成功后就会直接跳转到我们要登录的页面
# @method_decorator(login_auth, name='post')
# @method_decorator(login_auth, name='get')

class MyOrder(View):
    # 将装饰器传入进去
    # @method_decorator(login_auth)
    # 我们可以重写dispatch函数来实现
    def dispatch(self, request, *args, **kwargs):
        # 我们可以重写dispatch函数来实现类似于装饰器的效果
        # dispatch 内部根据反射来实现函数执行
        # 集成父类view的属性
        ret = super().dispatch(request, *args, **kwargs)
        return ret
    # 在cbv上加装饰器,需要用method_decorator修饰一下
    @method_decorator(login_auth)
    def get(self, request):
        return HttpResponse('get')
    def post(self,request):
        return HttpResponse('post')
    
'''
1 导入from django.utils.decorators import method_decorator
2 加载get,post,dispatch方法上:@method_decorator(login_auth)
3 加在类上:@method_decorator(login_auth,name='get')
'''

  

四,cookie + session

1,cookie引入session

  cookie看似解决了HTTP(短连接,无状态)的会话保持问题,但把全部用户数据保存在客户端,存在安全隐患。

  于是cookie+session出现了,我们,我们可以把关于用户的数据保存在服务端,在客户端cookie里加一个sessionID(随机字符串)。

  基于以上原因:cookie+session组成就成立了,并且结束了单单使用cookie做会话保持的方式。

2,Session和Cookie的好处

  使用Session和Cookie的好处:Cookie可以理解为一个身份证ID,你只要拿着他去和Server端进行通信,如果你没有这个ID,那么server端也不知道你是谁。

  举个例子:我在写博客的时候,在做Cookie和Session的实验,把Cookie删掉了,当我保存的时候直接给我提出来了,为什么呢?就是因为server端不知道我是谁了,因为我已经没有秘钥了。

  所以,只要Session和Cookie任意一方失效,就可以理解为:Cookie失效相当于身份证ID过期,需要重新认证才可以继续使用。Session失效就相当于银行里的数据标识此ID无效,也需要重新申请。

3,cookie+session 的工作流程

1,当用户来访问服务端的时候,服务端生成一个随机字符串;

2,当用户登录成功后,把{sessionID : 随机字符串} 组织成键值对 加到cookie里发送给用户;

3,服务器以发送给客户端cookie中的随机字符串做键,用户信息做值,保存用户信息;

 

 

代码梳理

用户登录的两种方式:

1,使用login() 和logout()这两个内置函数实现登录和退出;缺点就是用户的登录时在操作同一个request.user。导致同一台电脑上不能同时登录两个用户;

2,如果同一台电脑的同一个网站,登录多个账号,为了防止串号,不能再使用login() 和logout()函数了,可以通过session和cookie来实现这个需求;

1,login.html

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>Title</title>
</head>
<body>

    <form action="login.html" method="post">
        <input type="text" name="username">
        <input type="password" name="pwd">
        <input type="submit" value="submit">
    </form>

</body>
</html>

  

2,home.html

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>Title</title>

    <style>
        .header{
            height: 48px;
        }
    </style>

</head>
<body>

    <div class="header">
        <div style="float: right;">{{ username }}</div>
        <div style="float:right;"><a href="logout.html">注销</a></div>
    </div>

    <div style="height: 500px; background-color: #ddd"></div>

</body>
</html>

  

3,view.py

from django.shortcuts import render,redirect,HttpResponse

# Create your views here.

def login(request):
    if request.method == 'POST':
        username = request.POST.get('username')
        password = request.POST.get('password')
        if username == 'james' and password == '123':
            # 设置session
            request.session['IS_Login'] = True
            request.session['USERNAME'] = 'james'
            return redirect('/Manytable/home/')
        elif username == 'durant' and password == '123':
            request.session['IS_LOGIN'] = True
            request.session['USERNAME'] = 'durant'
            return redirect('/Manytable/home/')

    return render(request, 'Manytable/login.html')

def home(request):
    # 获取session
    is_login = request.session.get("IS_LOGIN", False)
    if is_login:
        username = request.session.get("USERNAME", False)
        return render(request, 'Manytable/home.html', {'username':username})
        # return HttpResponse('order')
    else:
        return redirect('/Manytable/login/')

  

4,mysite/urls.py

from django.contrib import admin
from django.urls import path
from django.conf.urls import url
# 需要先导入对应的app的views文件

from Manytable import views

# admin 后台的路由,先注释掉
urlpatterns = [
    url(r'^admin/', admin.site.urls),
    # 你的路由,重点是引号中的正则表达式和后面的业务逻辑函数
    url(r'^login/',views.login),
    url(r'^index/',views.home),
]

  

5,格式如下:

Django学习笔记(5)——cookie和session

 

 

 作业练习

1,登录案例

需要的页面:
#login.html:登录页面,提供登录表单;

#index1.html:主页,显示当前用户名称,如果没有登录,显示您还没登录;

#index2.html:主页,显示当前用户名称,如果没有登录,显示您还没登录;

  思考:如果第二个人在同一个浏览器上登录,Django-session表会怎么样呢?

2,验证码案例

  验证码可以去识别发出请求的是人还是程序!当然,如果聪明的程序可以去分析验证码图片!但是分析图片也不是一件容易的事情,因为一般验证码图片都会带有干扰线,人都看不清,那么程序一定分析不出来。

 

 

参考文献:https://www.cnblogs.com/chenchao1990/p/5283725.html

https://blog.csdn.net/qq_32446743/article/details/79482536

https://www.jianshu.com/p/59cb3ecd81a4

https://www.cnblogs.com/sui776265233/p/9643055.html