Cookie、Session、Token的由来

我们知道HTTP协议无连接的, 也就是不保存用户的状态信息

早期(十几年前)的网页是静态的, 数据都是写死的, 人们访问网页只是用来查看新闻的, 没有保存用户状态的需求

而往后出现了像论坛、博客、网购这一类需要保存用户信息的网站, 如果网站不保存用户的状态信息, 意味着用户每次访问都需要重新输入用户名和密码, 这无疑对用户的体验是极其不好的

于是, 就出现了会话跟踪技术, 我们可以把它理解为客户端与服务端之间的一次会晤, 一次会晤包含的多次请求与响应, 每次请求都带着请求参数, 比如请求登入的请求参数是用户名和密码, 服务端就会拿着请求参数与数据库去比对, 找到相应的用户信息

如何实现会话跟踪 : 在HTTP协议中可以使用Cookie来完成, 在Web开发中可以使用Session来完成

  • Cookie是存在浏览器中的键值对, 每次发送请求都携带者参数, 但是容易被截获, 不安全
  • 于是就出现了Session, 它是存在于服务端的键值对, key为随机字符串, 安全性提高了, 但所有的数据都存在服务器中, 服务器的压力很大
  • 之后便产生了Token的概念, 服务端签发加密后的字符串给客户端浏览器保存, 客户端每次请求携带用户名和密码, 并加上由服务端签发的用户名和密码加密的字符串, 服务端收到请求后再对用户名密码加密, 与后面携带的密文对比, 由于它也是保存在客户端浏览器上的, 所以也叫Cookie

Cookie简介

什么是Cookie

  • Cookie是服务器保存在客户端浏览器之上的key-value键值对 : username='shawn';password="123"
  • 它是随着服务器的响应发送给客户端, 客户端将其保存, 下一次请求时会将Cookie放在其中, 服务器通过识别Cookie就能知道是哪个客户端浏览器

Cookie规范

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

上面是HTTP中Cookie的规范, 现在浏览器的竞争, 有些Cookie大小能打到8KB, 最多可以保存500个Cookie

不同浏览器之间的Cookie是不共享的

安全性

  • Cookie保存在浏览器本地, 意味着很容易被窃取和篡改

Session简介

什么是Session

  • 存放在服务器上的键值对
  • Cookie可以保存状态, 但本身最大只能支持4069字节, 并且不安全, 于是就出现了Session
  • 它能支持更多字节, 并且保存在服务器上, 具有较高的安全性,Session基于Cookie, 本地存放服务器返回给浏览器的随机字符串
  • 客户端浏览器请求中携带随机字符串(session_id), 服务端收到后与数据库中存储的session做对比

Cookie与Session区别

  1. cookie是存在于浏览器上的, 保存形式以key:value键值对的形式
  2. session是存在于服务端的, django中保存在django_session表中, key: 对应session_key字段, value: 对应session_data, 还有一个session_date用来保存终止会话时间 (默认14天)
  3. session是基于cookie工作的, 在djangosession会告知浏览器以sessionid:随机字符的格式保存数据

Token简介

什么是Token

  • session数据保存在服务端, 提升了安全性, 但如果用户数据量特别多, 那么服务器的压力就会非常大
  • 所以不再在服务端中保存数据
  • Token采用了jwt(json web token)的认证方式, 数据格式分为三段式 :Header(头部)、Payload(负载)、Signature(签名)
  • 登入成功之后, 将第一段数据与第二段数据进行加密(加密算法是你后端开发自定义的), 加密后得到的字符串放在最后一段, 然后整体的返回给浏览器
  • 浏览器下次访问的时候就会带着该信息, 服务端收到再取前两段进行加密与第三段对比

Django中cookie的使用

关键字:res.set_cookie('key','value)

def index(request):
    res = HttpResponse('生成了cookie')
    res.set_cookie('name', 'kevin')

    return res

关键字: request.COOKIE.get('key')

def index(request):
    res = request.COOKIES.get('name')
    print(res)
    return HttpResponse('ok')

关键字: res.set_cookie('key','value')

def index(request):
    res = HttpResponse('重新生成了cookie')
    res.set_cookie('name', 'kevin666')
    return res

关键字 : res.delete_cookie('key')

def index(request):
    res = HttpResponse('删除了cookie')
    res.delete_cookie('name')
    return res

cookie加盐

def index(request):
    res = HttpResponse('加盐生成cookie')
    # 加盐操作
    res.set_signed_cookie('name', 'kevin', salt='加点盐',max_age=3)  # max_age 超时时间:默认是秒数
    
    return res

Django中Session的使用

关键字:request.session['key'] = value

def index(request):
    request.session['name'] = 'kevin'
    return HttpResponse('设置了一个session')
  # session信息保存在了自带的 django_session 表

删(客户端)

关键字:request.session.delete()

def index(request):
      request.session.delete()
      return HttpResponse('客户端删除成功')

删(服务端、客户端)

关键字:request.session.flush()

def index(request):
    request.session.flush()
    return HttpResponse('客户端、服务端删除成功')

关键字:request.session['key'] = value

def index(request):
    name1 = request.session.get('name')
    request.session['name'] = 'kevin666'
    name2 = request.session.get('name')
    return HttpResponse(f'修改前{name1},修改后{name2}')

关键字:request.session.session_key

def index(request):
    res1 = request.session.get('name')
    res2 = request.session.session_key  # 获取产生的随机字符串
    print(res1, res2)
    return HttpResponse('获取session')

补充

request.session.set_expiry(value) 设置超时时间

  • 如果value是个整数,session会在些秒数后失效
  • 如果value是个datatimetimedeltasession就会在这个时间后失效
  • 如果value是0,用户关闭浏览器session就会失效
  • 如果valueNone,session会依赖全局session失效策略

基于cookie的登入认证示例

urls.py

from django.conf.urls import url
from app01 import views

urlpatterns = [
    url(r'^login/', views.login),
    url(r'^home/', views.home),
    url(r'^book/', views.book),
    url(r'^logout/', views.logout),

]

views.py

from django.shortcuts import redirect, HttpResponse, render


def login_auth(func_name):
    def inner(request, *args, **kwargs):
        # 获取用户没有登录之前想要访问的网址地址
        target_path = request.get_full_path()
        if request.COOKIES.get('name') == 'kevin':
            res = func_name(request, *args, **kwargs)
            return res
        else:
            return redirect(f'/login/?target={target_path}')

    return inner


def login(request):
    # 判断是否登录过(获取cookie)
    if request.COOKIES.get('name') == 'kevin':
        return redirect('/home/')
    if request.method == 'POST':
        username = request.POST.get('username')
        password = request.POST.get('password')
        if username == 'kevin' and password == '123':
            # 获取用户之前想访问的地址,登录跳转
            target_path = request.GET.get('target')
            if target_path:
                res = redirect(f'{target_path}')
            else:
                # 默认登录之后跳转的页面
                res = redirect('/home/')
            # 登录成功cookie保存到浏览器
            res.set_cookie('name', 'kevin')
            return res
    return render(request, 'login.html')


@login_auth
def home(request):
    return HttpResponse('这是home页面,只有登录的用户可以查看')


@login_auth
def book(request):
    return HttpResponse('这是book页面,只有登录的用户可以查看')


# 退出登录
def logout(request):
    res = redirect('/login/')
    # 删掉cookie
    res.delete_cookie('name')
    return res

login.html

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>Title</title>
    <meta name="viewport" content="width=device-width, initial-scale=1">
    <link href="https://cdn.bootcdn.net/ajax/libs/twitter-bootstrap/3.4.1/css/bootstrap.min.css" rel="stylesheet">
    <script src="https://cdn.bootcdn.net/ajax/libs/twitter-bootstrap/3.4.1/js/bootstrap.min.js"></script>
</head>
<body>
<div class="container">
    <div class="row">
        <div class="col-lg-6 col-md-offset-3" style="max-width: 300px">
            <h3 class="text-center">登录</h3>
            <form action="" method="post">
                <p>username:
                    <input type="text" class="form-control" name="username">
                </p>
                <p>password:
                    <input type="password" class="form-control" name="password">
                </p>
                <input type="submit" class="btn btn-success btn-block ">
            </form>
        </div>
    </div>
</div>
</body>
</html>

基于session的登入认证示例

from django.shortcuts import redirect, HttpResponse, render


def login_auth(func_name):
    def inner(request, *args, **kwargs):
        target_path = request.get_full_path()
        if request.session.get('name') == 'kevin':
            return func_name(request, *args, **kwargs)
        return redirect(f'/login/?next={target_path}')

    return inner


def login(request):
    if request.session.get('name') == 'kevin':
        return redirect('/home/')
    if request.method == 'POST':
        username = request.POST.get('username')
        password = request.POST.get('password')
        if username == 'kevin' and password == '123':
            request.session['name'] = username
            request.session['password'] = password
            target = request.GET.get('next')
            if target:
                return redirect(target)
            return redirect('/home/')
    return render(request, 'login.html')


@login_auth
def home(request):
    return HttpResponse('只有登录的用户能看到')


@login_auth
def book(request):
    return HttpResponse('只有登录的用户能看到')


@login_auth
def logout(request):
    request.session.flush()
    return redirect('/login/')

CBV添加装饰器

方式一

直接在类中的某个方法上添加

from django import views
from django.utils.decorators import method_decorator


class MyLoginView(views.View):
    @method_decorator(login_auth)
    def get(self, request):
        return HttpResponse('from CBV get view')

    def post(self, request):
        return HttpResponse('from CBV post view')

方式二

直接在类名上添加并指定

@method_decorator(login_auth, name='get')  # 如果get方法和post方法都需要校验的话就写两个装饰器
class MyLoginView(views.View):

    def get(self, request):
        return HttpResponse('from CBV get view')

    def post(self, request):
        return HttpResponse('from CBV post view')

方式三

重写dispatch方`法并添加作用于类中所有的方法

class MyLoginView(views.View):
    def get(self, request):
        return HttpResponse('from CBV get view')

    def post(self, request):
        return HttpResponse('from CBV post view')

    @method_decorator(login_auth)
    def dispatch(self, request, *args, **kwargs):
        return super().dispatch(request, *args, **kwargs)
Last modification:May 30, 2022
如果觉得我的文章对你有用,请随意赞赏