day72---Django的Cookie和Session

1. Cookie和Session介绍

  • cookie机制采用客户端保持状态的方案

  • session机制采用服务端保持状态的方案

Cookie介绍

  • 当浏览网站时,web服务器会先发送一些数据在计算机上,cookie会把这些网站上的这些信息记录下来,下次再次访问时,web服务器会先检查是否有之前记录的cookie信息,根据cookie中的内容进行判断,返回指定的页面

  • cookie机制:正统的cookie分发是通过扩展HTTP协议来实现的,服务器通过在HTTP的响应头中加上一行特殊的指示以提示浏览器按照指示生成相应的cookie。纯粹的客户端脚本也可以生成cookie。而cookie的使用是由浏览器按照一定的原则在后台自动发送给服务器的。浏览器检查所有存储的cookie,如果某个cookie所声明的作用范围大于等于将要请求的资源所在的位置,则把该cookie附在请求资源的HTTP请求头上发送给服务器

  • cookie的内容主要包括:名字、值、过期时间、路径和域名信息。路径与域一起构成cookie的作用范围。若不设置过期时间,则表示这个cookie的生命期为浏览器会话期间,关闭浏览器窗口,cookie就消失。这种生命期为浏览器会话期的cookie被称为会话cookie。会话cookie一般不存储在硬盘上而是保存在内存里,当然这种行为并不是规范规定的。若设置了过期时间,浏览器就会把cookie保存到硬盘上,关闭后再次打开浏览器,这些cookie仍然有效直到超过设定的过期时间。存储在硬盘上的cookie可以在不同的浏览器进程间共享。而对于保存在内存里的cookie,不同的浏览器有不同的处理方式

Session介绍

  • session机制:session机制是一种服务器端的机制,服务器使用一种类似于散列表的结构来保存信息

  • 程序需要为某个客户端的请求创建一个session时,服务器首先检查这个客户端的请求里是否已包含了一个session标识(称为session id),如果已包含则说明以前已经为此客户端创建过session,服务器就按照session id把这个session检索出来使用,如果客户端请求不包含session id,则为此客户端创建一个session并且生成一个与此session相关联的session id,session id的值应该是一个既不会重复,又不容易被找到规律以仿造的字符串,这个session id将被在本次响应中返回给客户端保存。保存这个session id的方式可以采用cookie,这样在交互过程中浏览器可以自动的按照规则把这个标识发送给服务器。一般这个cookie的名字都是类似于SEEESIONID。但cookie可以被人为的禁止,则必须有其他机制以便在cookie被禁止时仍然能够把session id传递回服务器
  • Session的常用应用:
    • URL重写就是把session id直接附加在URL路径的后面
    • 表单隐藏字段就是服务器会自动修改表单,添加一个隐藏字段,以便在表单提交时能够把session id传递回服务器

cookie和session的区别

  • cookie数据存放在客户的浏览器上,session数据放在服务器上

  • cookie不是很安全,别人可以分析存放在本地的COOKIE并进行COOKIE欺骗,考虑到安全应当使用session

  • session会在一定时间内保存在服务器上。当访问量增多时,会比较占用服务器的性能,考虑到减轻服务器性能方面,应当使用COOKIE

  • 单个cookie保存的数据不能超过4K,很多浏览器都限制一个站点最多保存20个cookie

  • 存储重要信息或敏感信息时应该存放到session中

  • 需要保留的不具有危险性的信息应该存放到cookie中

2. Django中操作Cookie

描述:cookies是浏览器为Web服务器存储的信息。每次浏览器从服务器端请求页面时,会向服务器回送之前收到的cookies。cookies以键值对的方式保存在浏览器中

设置cookie

  • 明文的方式:set_cookie(key, value='', max_age=None, expires=None, path='/', domain=None, secure=False, httponly=False)
    • key:设置cookie的键名
    • values:设置cookie的值
    • max_age:设置cookie的超时时间
    • expires:设置cookie的超时时间(根据时间差计算后指定的时间)
    • path:设置cookie生效的路径,默认为根路径,可以访问任何url页面
    • domain:设置cookie生效的域名
    • secure:设置cookie支持https传输(True/False)
    • httponly`:设置cookie只能http传输(True/False)
  • 密文的方式:set_signed_cookie(key, value, salt='')
    • key:设置cookie的键名
    • values:设置cookie的值
    • salt:指定加密盐
    res = HttpResponse(...) # render(request, ...)
    res.set_cookie('key', value, ...)
    res.set_signed_cookie('key', 'value', salt='加密盐')

获取cookie

  • 明文的方式:request.COOKIES.get[key]

    • key:指定获取cookie的键名,返回对应的值
  • 密文的方式:request.get_signed_cookie(key, default=RAISE_ERROR, salt='', max_age=None)
    • key:指定获取cookie的键名,返回对应的值
    • default:默认值(建议使用默认参数)
    • salt:指定加密盐用于获取键值
    • max_age:超时时间(建议使用默认参数None)
request.COOKIE.get('key')
request.get_signed_cookie('key', None, salt='加密盐')

删除cookie

删除浏览器中设置的cookie值:delete_cookie(key)

cookie的优缺点

  • 优点:数据存在在客户端,减轻服务器端的压力,提高网站的性能

  • 缺点:安全性不高,在客户端很容易被查看或破解用户会话信息

cookie版登录校验

    # urls.py
    from django.conf.urls import url
    from app import views
    urlpatterns = [
        url(r'^login/$', views.login),
        url(r'^logout/$', views.logout),
        url(r'^index/$', views.index),
        url(r'^home/$', views.home),
    ]
    # views.py
    from django.shortcuts import render, redirect
    from functools import wraps
    user = 'aaa'
    pwd = '123'
    def wrapper(func):
        @wraps(func)
        # wraps是修复装饰器用的,可以显示原函数的__name__和__doc__,默认显示的是inner
        def inner(request, *args, **kwargs):
            # 获取当前访问的url
            get_url = request.get_full_path()
            # 获取cookie
            cookie = request.get_signed_cookie('login', None, salt='yan')
            if cookie:
                # 登录成功,跳转对应的页面
                res = func(request, *args, **kwargs)
                return res
            else:
                # 登录失败,跳转登录页面
                return redirect('/login/?from={}'.format(get_url))
        return inner
    def login(request):
        # 获取登陆前的url
        get_url = request.GET.get('from')
        if request.method == 'POST':
            # 获取用户名和密码
            username = request.POST.get('username')
            password = request.POST.get('password')
            if username == user and password == pwd:
                # 如果当前url有参数登录成功之后跳转之前的页面
                if get_url:
                    res = redirect(get_url)
                # 如果当前url没有参数登录成功之后跳转index页面
                else:
                    res = redirect('/index/')
                # 设置cookie
                res.set_signed_cookie('login', username, salt='yan', max_age=10)
                return res
        return render(request, 'login.html')
    def logout(request):
        res = redirect('/login/')
        res.delete_cookie('login')
        return res
    def index(request):
        return render(request, 'index.html')
    @wrapper
    def home(request):
        return render(request, 'home.html')
    <!-- login.html -->
	<!DOCTYPE html>
	<html lang="en">
	<head>
		<meta charset="UTF-8">
		<meta http-equiv="X-UA-Compatible" content="IE=edge">
		<meta name="viewport" content="width=device-width, initial-scale=1">
		<title>login</title>
	</head>
	<body>
	<div>
		<form action="{{ request.get_full_path }}" method="post">
			{% csrf_token %}
			<label for="username">用户名:</label>
			<input id="username" type="text" name="username">
			<p></p>
			<label for="password">密码:</label>
			<input id="password" type="password" name="password">
			<p></p>
			<input type="submit" value="提交">
			<input type="reset" value="取消">
		</form>
	</div>
	</body>
	</html>
    <!-- index.html -->
    <!DOCTYPE html>
    <html lang="en">
    <head>
        <meta charset="UTF-8">
        <meta http-equiv="X-UA-Compatible" content="IE=edge">
        <meta name="viewport" content="width=device-width, initial-scale=1">
        <title>index</title>
    </head>
    <body>
    <h1>this is index page...</h1>
    </body>
    </html>
    <!-- home.html -->
    <!DOCTYPE html>
    <html lang="en">
    <head>
        <meta charset="UTF-8">
        <meta http-equiv="X-UA-Compatible" content="IE=edge">
        <meta name="viewport" content="width=device-width, initial-scale=1">
        <title>home</title>
    </head>
    <body>
    <h1>welcome home page !!!</h1>
    </body>
    </html>

3. Django中操作Session

session介绍

  • 描述:Session依赖于Cookie,以键值对的形式保存在服务端

  • session说明:

    • 客户端登录Web服务器,会生成一个键值对形式的数据,存储在session中
    • key是自动生成的一段字符串标识,返回给客户端当作cookie值存储在浏览器中
    • value是一个自定义格式的字典形式,用来存储客户端的用户信息
  • Django中的session说明:

    • django中用到session时,cookie是由服务端随机生成,然后返回后浏览器存储到cookie中(response处理响应)
    • 每一个浏览器都有自己的cookies值,这个值是session查找的唯一标识
    • 浏览器每次向服务端发送请求,服务端接收的request.session就是服务端的session中key(cookie)对应的value

session的基本操作

  • 设置session:

    • request.session['key'] = value:直接设置session键值对
    • reqeust.session.setdefault['key'] = (value, default_value):如果session中key已经存在,则不操作,如果key不存在,则设置key对应的值为第二个参数default_value
  • 获取session:

    • request.session['key']:直接获取session中key对应的值
    • request.session.get('key', None):如果session中key存在,则返回对应的值,如果key不存在,则返回第二个参数None
  • 删除session:

    • del request.session['key']
    • request.session.pop('key')
  • 获取session所有的键:

    • request.session.keys():返回列表
    • request.session.iterkeys():返回迭代器
  • 获取session所有的值:

    • request.session.values():返回列表
    • request.session.itervalues():返回迭代器
  • 获取session所有的键值对:

    • request.session.item():返回列表
    • request.session.iteritems():返回迭代器
  • 获取session的随机字符串:request.session.session_key

  • 判断指定会话的session随机字符串是否存在:request.session.exists("session_key")

  • 删除已过期的session:request.session.clear_expired()

  • 删除当前会话的所有session数据:request.session.delete("session_key")

  • 删除当前的会话数据并删除会话的cookie:request.session.flush()

  • 设置session的过期时间:request.session.set_expiry(value)

    • 如果value是整数,则session的过期时间就是指定的秒数
    • 如果value是0,则每次关闭浏览器session就会过期
    • 如果value是datatime或timedelta的对象,则session的过期时间就是过了这个时间点
    • 如果value是None,则session的过期时间依赖全局的配置(setting.py中的配置)

Django中支持的session存储类型

  • 数据库存储类型(db,默认类型)

    SESSION_ENGINE = 'django.contrib.sessions.backends.db'
    # 指定存储引擎
  • 缓存存储类型(cache)

    SESSION_ENGINE = 'django.contrib.sessions.backends.cache'
    SESSION_CACHE_ALIAS = 'default'
    # 指定使用的缓存别名(默认为内存缓存,可以使用缓存服务器)
  • 文件存储类型(file)

    SESSION_ENGINE = 'django.contrib.sessions.backends.file'
    SESSION_FILE_PATH = None   
    # 指定缓存文件路径(如果设置为None,则使用tempfile模块获取一个临时地址tempfile.gettempdir())
  • 缓存加数据库结合存储类型(cached_db)

    SESSION_ENGINE = 'django.contrib.sessions.backends.cached_db'
  • 加密Cookie和Session的存储类型(signed_cookies)

    SESSION_ENGINE = 'django.contrib.sessions.backends.signed_cookies'

session的默认配置

    SESSION_COOKIE_NAME = "sessionid"
    # Session的cookie保存在浏览器上时的key,默认是sessionid
    SESSION_COOKIE_PATH = "/"
    # Session的cookie保存的路径,默认是'/'
    SESSION_COOKIE_DOMAIN = None
    # Session的cookie保存的域名,默认是None
    SESSION_COOKIE_SECURE = False
    # 是否启用Https传输cookie,默认是不启用
    SESSION_COOKIE_HTTPONLY = True
    # 是否启用Session的cookie只支持http传输,默认开启
    SESSION_COOKIE_AGE = 1209600
    # Session的cookie失效日期,默认是14天(2周)
    SESSION_EXPIRE_AT_BROWSER_CLOSE = False
    # 是否启用关闭浏览器使Session过期,默认不启用
    SESSION_SAVE_EVERY_REQUEST = False
    # 是否启用每次请求都保存Session,默认不启用(修改之后才保存),建议设置为True

session的优缺点

  • 优点:客户端只存储cookie的值,没有其他的信息,提高了安全性

  • 缺点:访问量过高时,会占用服务器性能

session版登录校验

    # urls.py
    from django.conf.urls import url
    from app import views
    urlpatterns = [
        url(r'^login/$', views.login),
        url(r'^logout/$', views.logout),
        url(r'^index/$', views.index),
        url(r'^home/$', views.home),
    ]
    # views.py
    from django.shortcuts import render, redirect
    from functools import wraps
    user = 'aaa'
    pwd = '123'
    def wrapper(func):
        def inner(request, *args, **kwargs):
            # 获取当前访问的url
            get_url = request.get_full_path()
            # 获取session
            session = request.session.get('login', None)
            if session:
                # 登录成功,跳转对应的页面
                res = func(request, *args, **kwargs)
                return res
            else:
                # 登录失败,跳转登录页面
                return redirect('/login/?from={}'.format(get_url))
        return inner
    def login(request):
        # 获取登陆前的url
        get_url = request.GET.get('from')
        if request.method == 'POST':
            # 获取用户名和密码
            username = request.POST.get('username')
            password = request.POST.get('password')
            if username == user and password == pwd:
                # 如果当前url有参数登录成功之后跳转之前的页面
                if get_url:
                    res = redirect(get_url)
                # 如果当前url没有参数登录成功之后跳转index页面
                else:
                    res = redirect('/index/')
                # 设置session
                request.session['login'] = username
                request.session.set_expiry(10)
                return res
        return render(request, 'login.html')
    def logout(request):
        res = redirect('/login/')
        res.delete_cookie('login')
        request.session.pop('login', None)
        return res
    def index(request):
        return render(request, 'index.html')
    @wrapper
    def home(request):
        return render(request, 'home.html')
    <!-- login.html -->
	<!DOCTYPE html>
	<html lang="en">
	<head>
		<meta charset="UTF-8">
		<meta http-equiv="X-UA-Compatible" content="IE=edge">
		<meta name="viewport" content="width=device-width, initial-scale=1">
		<title>login</title>
	</head>
	<body>
	<div>
		<form action="{{ request.get_full_path }}" method="post">
			{% csrf_token %}
			<label for="username">用户名:</label>
			<input id="username" type="text" name="username">
			<p></p>
			<label for="password">密码:</label>
			<input id="password" type="password" name="password">
			<p></p>
			<input type="submit" value="提交">
			<input type="reset" value="取消">
		</form>
	</div>
	</body>
	</html>
    <!-- index.html -->
    <!DOCTYPE html>
    <html lang="en">
    <head>
        <meta charset="UTF-8">
        <meta http-equiv="X-UA-Compatible" content="IE=edge">
        <meta name="viewport" content="width=device-width, initial-scale=1">
        <title>index</title>
    </head>
    <body>
    <h1>this is index page...</h1>
    </body>
    </html>
    <!-- home.html -->
    <!DOCTYPE html>
    <html lang="en">
    <head>
        <meta charset="UTF-8">
        <meta http-equiv="X-UA-Compatible" content="IE=edge">
        <meta name="viewport" content="width=device-width, initial-scale=1">
        <title>home</title>
    </head>
    <body>
    <h1>welcome home page !!!</h1>
    </body>
    </html>

4. CBV实现登录视图

    # urls.py
    from django.conf.urls import url
    from app import views
    urlpatterns = [
        url(r'^login/$', views.LoginView.as_view(), name='login'),
        url(r'^logout/$', views.LogoutView.as_view(), name='logout'),
        url(r'^index/$', views.IndexView.as_view(), name='index'),
        url(r'^home/$', views.HomeView.as_view(), name='home'),
    ]
    # views.py
    from django.shortcuts import render, redirect
    from django.urls import reverse
    from functools import wraps
    from django.views import View
    from django.utils.decorators import method_decorator
    user = 'aaa'
    pwd = '123'
    def wrapper(func):
        @wraps(func)
        def inner(request, *args, **kwargs):
            get_url = request.get_full_path()
            session = request.session.get('user', None)
            if session:
                res = func(request, *args, **kwargs)
                return res
            else:
                return redirect('{0}?from={1}'.format(reverse('login'), get_url))
        return inner
    class LoginView(View):
        def get(self, request):
            return render(request, 'login.html')
        def post(self, request):
            get_url = request.GET.get('from')
            username = request.POST.get('username')
            password = request.POST.get('password')
            if username == user and password == pwd:
                if get_url:
                    res = redirect(get_url)
                else:
                    res = redirect(reverse('index'))
                request.session['user'] = username
                request.session.set_expiry(60)
                return res
            else:
                return render(request, 'login.html')
    class LogoutView(View):
        def get(self, request):
            res = redirect(reverse('login'))
            res.delete_cookie('user')
            request.session.pop('user', None)
            return res
    class IndexView(View):
        def get(self, request):
            return render(request, 'index.html')
    class HomeView(View):
        @method_decorator(wrapper)
        def get(self, request):
            return render(request, 'home.html')
    <!-- login.html -->
    <!DOCTYPE html>
    <html lang="en">
    <head>
        <meta charset="UTF-8">
        <meta http-equiv="X-UA-Compatible" content="IE=edge">
        <meta name="viewport" content="width=device-width, initial-scale=1">
        <title>login</title>
    </head>
    <body>
    <div>
        <form action="{{ request.get_full_path }}" method="post">
            {% csrf_token %}
            <label for="username">用户名:</label>
            <input id="username" type="text" name="username">
            <p></p>
            <label for="password">密码:</label>
            <input id="password" type="password" name="password">
            <p></p>
            <input type="submit" value="提交">
            <input type="reset" value="取消">
        </form>
    </div>
    </body>
    </html>
    <!-- index.html -->
    <!DOCTYPE html>
    <html lang="en">
    <head>
        <meta charset="UTF-8">
        <meta http-equiv="X-UA-Compatible" content="IE=edge">
        <meta name="viewport" content="width=device-width, initial-scale=1">
        <title>index</title>
    </head>
    <body>
    <h1>this is index page...</h1>
    </body>
    </html>
    <!-- home.html -->
    <!DOCTYPE html>
    <html lang="en">
    <head>
        <meta charset="UTF-8">
        <meta http-equiv="X-UA-Compatible" content="IE=edge">
        <meta name="viewport" content="width=device-width, initial-scale=1">
        <title>home</title>
    </head>
    <body>
    <h1>welcome home page !!!</h1>
    <p></p>
    <h2><a href="{% url 'logout' %}">logout</a></h2>
    </body>
    </html>

5. 给CBV视图添加装饰器

给CBV视图添加装饰器有三种方式,需要导入method_decorator模块

    from django.utils.decorators import method_decorator

在CBV视图的get或post方法上添加

只有添加之后的方法才会生效,不添加的不会生效

    class ShowView(View):
        @method_decorator(wrapper)
        def get(self, request):
            return render(request, 'show.html')
        @method_decorator(wrapper)
        def post(self, request):
            return redirect('/show/')

在CBV视图类上添加

需要在method_decorator中指定name参数,name的值为类中的方法名

    @method_decorator(wrapper, name='get')
    @method_decorator(wrapper, name='post')
    class ShowView(View):
        def get(self, request):
            return render(request, 'show.html')
        def post(self, request):
            return redirect('/show/')

在dispatch方法上添加

在CBV中先执行的是dispatch方法,给dispatch方法添加之后就代表给get和post方法都添加了

    class ShowView(View):
    @method_decorator(wrapper)
        def dispatch(self, request, *args, **kwargs):
            return super(ShowView, self).dispatch(request, *args, **kwargs)
        def get(self, request):
            return render(request, 'show.html')
        def post(self, request):
            return redirect('/show/')
  • CSRF_token相关装饰器在CBV只能加到dispatch方法上

    • csrf_protect:为当前函数强制设置防跨站请求伪造功能,即便settings中没有设置全局中间件
        from django.views.decorators.csrf import csrf_protect
    
    • csrf_exempt:取消当前函数防跨站请求伪造功能,即便settings中设置了全局中间件
        from django.views.decorators.csrf import csrf_exempt
    
posted @ 2018-01-26 14:43  _岩哥  阅读(103)  评论(0)    收藏  举报