Cookie,Session

cookie

一,Cookie的由来

HTTP协议是无状态的。

无状态的意思是每次请求都是独立的,它的执行情况和结果与前面的请求和之后的请求都无直接关系,它不会受前面的请求响应情况直接影响,也不会直接影响后面的请求响应情况。

一句有意思的话来描述就是人生只如初见,对服务器来说,每次的请求都是全新的。

状态可以理解为客户端和服务器在某次会话中产生的数据,那无状态的就以为这些数据不会被保留。会话中产生的数据又是我们需要保存的,也就是说要“保持状态”。因此Cookie就是在这样一个场景下诞生。

二,什么是Cookie

Cookie具体指的是一段小信息,它是服务器发送出来存储在浏览器上的一组组键值对,下次访问服务器时浏览器会自动携带这些键值对,以便服务器提取有用信息。

三,Cookie的原理

cookie的工作原理是:由服务器产生内容,浏览器收到请求后保存在本地;当浏览器再次访问时,浏览器会自动带上Cookie,这样服务器就能通过Cookie的内容来判断这个是“谁”了。

四,查看Cookie

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

Django中操作Cookie

一,获取Cookie

Cookie的内容时一个大字典,取值可按照字典的取值方式取值;

request.COOKIES['key']
request.get_signed_cookie(key, default=RAISE_ERROR, salt='', max_age=None)  # 取值时,注意将 default=RAISE_ERROR,修改其他值,如 default='',不然取不到会抛出异常
    def get_signed_cookie(self, key, default=RAISE_ERROR, salt='', max_age=None):
        """
        Attempts to return a signed cookie. If the signature fails or the
        cookie has expired, raises an exception... unless you provide the
        default argument in which case that value will be returned instead.
        """
        try:
            cookie_value = self.COOKIES[key]
        except KeyError:
            if default is not RAISE_ERROR:
                return default
            else:
                raise
        try:
            value = signing.get_cookie_signer(salt=key + salt).unsign(
                cookie_value, max_age=max_age)
        except signing.BadSignature:
            if default is not RAISE_ERROR:
                return default
            else:
                raise
        return value
get_signed_cookie源码

参数:

  • default: 默认值
  • salt: 加密盐
  • max_age: 后台控制过期时间

二,设置Cookie

rep = HttpResponse(...)
rep = render(request, ...)
rep = redirect("/xxx/") rep.set_cookie(key,value,...) rep.set_signed_cookie(key,value,salt='加密盐',...)
 def set_cookie(self, key, value='', max_age=None, expires=None, path='/',
                   domain=None, secure=False, httponly=False):
        """
        Sets a cookie.

        ``expires`` can be:
        - a string in the correct format,
        - a naive ``datetime.datetime`` object in UTC,
        - an aware ``datetime.datetime`` object in any time zone.
        If it is a ``datetime.datetime`` object then ``max_age`` will be calculated.
        """
        value = force_str(value)
        self.cookies[key] = value
        if expires is not None:
            if isinstance(expires, datetime.datetime):
                if timezone.is_aware(expires):
                    expires = timezone.make_naive(expires, timezone.utc)
                delta = expires - expires.utcnow()
                # Add one second so the date matches exactly (a fraction of
                # time gets lost between converting to a timedelta and
                # then the date string).
                delta = delta + datetime.timedelta(seconds=1)
                # Just set max_age - the max_age logic will set expires.
                expires = None
                max_age = max(0, delta.days * 86400 + delta.seconds)
            else:
                self.cookies[key]['expires'] = expires
        else:
            self.cookies[key]['expires'] = ''
        if max_age is not None:
            self.cookies[key]['max-age'] = max_age
            # IE requires expires, so set it if hasn't been already.
            if not expires:
                self.cookies[key]['expires'] = cookie_date(time.time() +
                                                           max_age)
        if path is not None:
            self.cookies[key]['path'] = path
        if domain is not None:
            self.cookies[key]['domain'] = domain
        if secure:
            self.cookies[key]['secure'] = True
        if httponly:
            self.cookies[key]['httponly'] = True
set_cookie源码
    def set_signed_cookie(self, key, value, salt='', **kwargs):
        value = signing.get_cookie_signer(salt=key + salt).sign(value)
        return self.set_cookie(key, value, **kwargs)
set_signed_cookie源码

参数:

  • key, 键
  • value='', 值
  • max_age=None, 超时时间
  • expires=None, 超时时间(IE requires expires, so set it if hasn't been already.)
  • path='/', Cookie生效的路径,/ 表示根路径,特殊的:根路径的cookie可以被任何url的页面访问
  • domain=None, Cookie生效的域名
  • secure=False, https传输
  • httponly=False 只能http协议传输,无法被JavaScript获取(不是绝对,底层抓包可以获取到也可以被覆盖)

删除Cookie

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

Cookie版登陆校验

from django.shortcuts import render, redirect
from functools import wraps
# Create your views here.


def check_login(func):
    @wraps(func)
    def inner(request, *args, **kwargs):
        print(request.COOKIES)
        # print("get_full_path", request.get_full_path())
        # print("path_info", request.path_info)
        next_url = request.path_info
        # if request.COOKIES.get("login", "") == "hello":
        if request.get_signed_cookie("login", salt="苟陇辉", default="") == "hello":
            return func(request, *args, **kwargs)
        else:
            return redirect("/login/?next={}".format(next_url))
    return inner


def login(request):
    err_msg = ""
    if request.method == "POST":
        username = request.POST.get("username")
        pwd = request.POST.get("password")
        if username == "gou" and pwd == "123":
            # print("request.GET:", request.GET)
            next_url = request.GET.get("next")  # 字典get方法取不到不会报错
            # print(next_url)
            if next_url:
                res = redirect(next_url)
            else:
                # 如果没有next_url,说明是直接访问的login函数
                res = redirect("/home/")
            res.set_cookie("login", "hello")
            res.set_signed_cookie("login", "hello", salt="苟陇辉", max_age=7)
            return res
        else:
            err_msg = "用户名或者密码错误"
    return render(request, 'login.html', {'err_msg': err_msg})


@check_login
def home(request):
    return render(request, "home.html")


@check_login
def course(request):
    return render(request, "course.html")


def login_out(request):
    res = redirect("/login/")
    res.delete_cookie("login")
    return res
views.py

Session

一,Session的由来

  Cookie虽然在一定程度上解决了“保持状态”的需求,但是由于Cookie本身最大支持4096字节,以及Cookie本身保存在客户端,可能被拦截或窃取,因此就需要有一种新的东西,它能支持更多的字节,并且他保存在服务器,有较高的安全性。这就是Session。

问题来了,基于HTTP协议的无状态特征,服务器根本就不知道访问者是“谁”。那么上述的Cookie就起到桥接的作用。

我们可以给每个客户端的Cookie分配一个唯一的id,这样用户在访问时,通过Cookie,服务器就知道来的人是“谁”。然后我们再根据不同的Cookie的id,在服务器上保存一段时间的私密资料,如“账号密码”等等。

总结而言:Cookie弥补了HTTP无状态的不足,让服务器知道来的人是“谁”;但是Cookie以文本的形式保存在本地,自身安全性较差;所以我们就通过Cookie识别不同的用户,对应的在Session里保存私密的信息以及超过4096字节的文本。

另外,上述所说的Cookie和Session其实是共通性的东西,不限于语言和框架。

Django中Session相关方法

# 获取、设置、删除Session中数据
request.session['k1']  # 获取
request.session.get('k1',None)  # 获取
request.session['k1'] = 123  # 设置
request.session.setdefault('k1',123) # 存在则不设置
del request.session['k1']  # 删除


# 所有 键、值、键值对
request.session.keys()
request.session.values()
request.session.items()
request.session.iterkeys()
request.session.itervalues()
request.session.iteritems()

# 会话session的key
request.session.session_key

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

# 检查会话session的key在数据库中是否存在
request.session.exists("session_key")

# 删除当前会话的所有Session数据
request.session.delete()
  
# 删除当前的会话数据并删除会话的Cookie。
request.session.flush() 
    这用于确保前面的会话数据不可以再次被用户的浏览器访问
    例如,django.contrib.auth.logout() 函数中就会调用它。

# 同时设置的是会话Session和Cookie的超时时间
request.session.set_expiry(value)
    * 如果value是个整数,session会在些秒数后失效。
    * 如果value是个datatime或timedelta,session就会在这个时间后失效。
    * 如果value是0,用户关闭浏览器session就会失效。
    * 如果value是None,session会依赖全局session失效策略。
注意:同一个浏览器如果在时间范围内,那么就算是关掉该浏览器,重新启动,session,cookie都存在,但是如果在换一个浏览器,
   那么因为该浏览器没有保存另外一个浏览器的cookie,所以需要重新登陆,重新生成session,cookie

Session流程解析

Django中的Session配置

Django中默认支持Session,其内部提供了5种类型的Session供开发者使用。

1. 数据库Session
SESSION_ENGINE = 'django.contrib.sessions.backends.db'   # 引擎(默认)

2. 缓存Session
SESSION_ENGINE = 'django.contrib.sessions.backends.cache'  # 引擎
SESSION_CACHE_ALIAS = 'default'                            # 使用的缓存别名(默认内存缓存,也可以是memcache),此处别名依赖缓存的设置

3. 文件Session
SESSION_ENGINE = 'django.contrib.sessions.backends.file'    # 引擎
SESSION_FILE_PATH = None                                    # 缓存文件路径,如果为None,则使用tempfile模块获取一个临时地址tempfile.gettempdir() 

4. 缓存+数据库
SESSION_ENGINE = 'django.contrib.sessions.backends.cached_db'        # 引擎

5. 加密Cookie Session
SESSION_ENGINE = 'django.contrib.sessions.backends.signed_cookies'   # 引擎

其他公用设置项:
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,默认修改之后才保存(默认)
Django中Session相关设置

Session版登陆验证(FBV)

from django.shortcuts import render, redirect
from functools import wraps
# Create your views here.


def check_login(func):
    @wraps(func)
    def inner(request, *args, **kwargs):
        print(request.COOKIES)
        # print("get_full_path", request.get_full_path())
        # print("path_info", request.path_info)
        next_url = request.path_info
        # if request.COOKIES.get("login", "") == "hello":
        if request.session.get("login", "") == "hello":
            return func(request, *args, **kwargs)
        else:
            return redirect("/login/?next={}".format(next_url))
    return inner


def login(request):
    err_msg = ""
    if request.method == "POST":
        username = request.POST.get("username")
        pwd = request.POST.get("password")
        if username == "gou" and pwd == "123":
            request.session["login"] = "hello"  # 设置session
            request.session.set_expiry(100)  # 设置session超出时间
            # print("request.GET:", request.GET)
            next_url = request.GET.get("next")
            # print(next_url)
            if next_url:
                res = redirect(next_url)
            else:
                # 如果没有next_url,说明是直接访问的login函数
                res = redirect("/home/")
            # res.set_cookie("login", "hello")
            # res.set_signed_cookie("login", "hello", salt="苟陇辉", max_age=7)
            return res
        else:
            err_msg = "用户名或者密码错误"
    return render(request, 'login.html', {'err_msg': err_msg})


@check_login
def home(request):
    return render(request, "home.html")


@check_login
def course(request):
    return render(request, "course.html")


def login_out(request):
    request.session.flush()  # 数据库中的内容也可以一起删除
    return redirect("/login/")
views.py

Session版登陆验证(CBV)

#!/usr/bin/env python
# -*- coding:utf8 -*-

from django.shortcuts import redirect, render
from django.utils.decorators import method_decorator
from django.views import View
from functools import wraps


def check_login(func):
    @wraps(func)
    def inner(request, *args, **kwargs):
        next_url = request.path_info
        # if request.COOKIES.get("login", "") == "hello":
        if request.session.get("login", "") == "hello":
            return func(request, *args, **kwargs)
        else:
            return redirect("/login/?next={}".format(next_url))
    return inner


class Login(View):
    err_msg = ""

    def get(self, request):
        return render(request, "login.html", {"err_msg": self.err_msg})

    def post(self, request):
        username = request.POST.get("username")
        pwd = request.POST.get("password")
        if username == "gou" and pwd == "123":
            request.session["login"] = "hello"
            request.session.set_expiry(100)
            next_url = request.GET.get("next")
            if next_url:
                res = redirect(next_url)
            else:
                res = redirect("/home/")
            return res
        else:
            self.err_msg = "用户名或者密码错误"
            return self.get(request)


class Home(View):
    @method_decorator(check_login)
    def get(self, request):
        return render(request, "home.html")

    def post(self, request):
        pass


class Course(View):
    @method_decorator(check_login)
    def get(self, request):
        return render(request, "course.html")

    def post(self, request):
        pass


class LoginOut(View):

    @staticmethod
    def get(request):
        request.session.flush()
        return redirect("/login/")

    def post(self, request):
        pass
view.py
"""cookies_test URL Configuration

The `urlpatterns` list routes URLs to views. For more information please see:
    https://docs.djangoproject.com/en/1.11/topics/http/urls/
Examples:
Function views
    1. Add an import:  from my_app import views
    2. Add a URL to urlpatterns:  url(r'^$', views.home, name='home')
Class-based views
    1. Add an import:  from other_app.views import Home
    2. Add a URL to urlpatterns:  url(r'^$', Home.as_view(), name='home')
Including another URLconf
    1. Import the include() function: from django.conf.urls import url, include
    2. Add a URL to urlpatterns:  url(r'^blog/', include('blog.urls'))
"""
from django.conf.urls import url
from django.contrib import admin
from app01 import views_cbv

urlpatterns = [
    url(r'^admin/', admin.site.urls),
    url(r'^login/', views_cbv.Login.as_view()),
    url(r'^home/', views_cbv.Home.as_view()),
    url(r'^course/', views_cbv.Course.as_view()),
    url(r'^login_out/', views_cbv.LoginOut.as_view()),
]
urls.py

 

posted @ 2018-08-06 21:46  Mr.GLH  阅读(311)  评论(0编辑  收藏  举报
Top↑