第十二篇:Django之cookie和session

第十二篇:Django之cookie和session

一、cookie简介

1、cookie的由来

一开始,网站都没有保存用户功能的需求。【类似新闻、文章、博客之类】

我们都知道HTTP协议是无状态的。

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

随着互联网的发展,出现了一些需要保存用户信息的网站。【类似淘宝、支付宝。京东之类的】

以登陆功能为例,如果不保存用户登陆状态,也就意味着用户每次访问网站都需要重复的输入用户名和密码(你觉得这样的网站你还想用吗?)

2、什么是cookie?

服务端保存在客户端浏览器上的信息都可以称之为cookie。它的表现形式一般都是k:v键值对(可以有多个)。

3、cookie的简单工作原理

简单来说,当用户第一次登陆成功之后,将用户的用户名密码返回给用户浏览器,让用户浏览器保存在本地,之后访问网站的时候浏览器自动将保存在浏览器上的用户名和密码发送给服务端,服务端获取之后自动验证。【当前,这种方式具有非常大的安全隐患】

虽然cookie是服务端告诉客户端浏览器需要保存内容,但是客户端浏览器可以选择拒绝保存,如果禁止了,那么只要是需要记录用户状态的网站登陆功能都无法使用了。

二、Django操作cookie

1、操作cookie方式

平时我们往前端返回数据,需要采用如下的方式。

# 视图函数的返回值
return HttpResponse()
return render()
return redirect()

"""如果你想要操作cookie,你就不得不利用obj对象"""
obj1 = HttpResponse()
# 操作cookie
return obj1

obj2 = render()
# 操作cookie
return obj2

obj3 = redirect()
# 操作cookie
return obj3
  • 添加cookie

    # 以键值对的方式设置cookie
    obj.set_cookie(key,value)
    
    # 还可以添加其他参数
    obj.set_cookie('username', 'yangyi',max_age=3,expires=3)
    	- max_age 两者都是设置超时时间的,并且都是以秒为单位
        - expires 只不过,针对IE浏览器需要使用expires
    
    # 加盐
    obj.set_signed_cookie(key, value, salt='盐')
    
  • 获取cookie

    # request.COOKIES获得浏览器发来的cookie数据,是一个字典
    request.COOKIES.get(key)  
    
    # 获取加盐数据
    obj.get_signed_cookie(key, salt='盐')
    
  • 删除cookie

    # 注销功能使用,但除了前端的cookie,就必须重新登陆了
    obj.delete_cookie('username')
    

接下来,我们进行一个小小的例子,帮助我们掌握cookie的知识点。

我们先简单建立一个登录界面。

文件路径和代码如下。

"""urls.py"""
urlpatterns = [
    url(r'^admin/', admin.site.urls),
    # 登录页面
    url(r'^login/', views.login),
    # 主页,登陆成功进行跳转
    url(r'^home/', views.home)
]


"""login.html"""
<div class="container">
        <div class="row">
            <div class="col-md-8 col-md-offset-2">
                <h1 class="text-center">登录系统</h1>
                <form action="" method="post">
                     <p>username:<input type="text" class="form-control" name="username"></p>
                     <p>password:<input type="text" class="form-control" name="password"></p>
                     <input type="submit" class="btn btn-primary btn-block">
                </form>
            </div>
        </div>
    </div>


"""views.py"""
# 登录
def login(request):
    # 如果是post请求
    if request.method == 'POST':
        # 拿到前端提交的数据
        username = request.POST.get('username')
        password = request.POST.get('password')
        # 进行一个简单的逻辑判断【正式的情况下,使用数据库】
        if username == 'yangyi' and password == '123':
            return redirect('/home/')
    return render(request, 'login.html')

# 主页
def home(request):
    return HttpResponse('我是主页,需要登陆成功才可以进来呦~')

到了这里,我们确实搭建了一个简单的登录系统,登陆成功之后,会跳转到/home/页面中,但是用户要是直接输入127.0.0.1:8000/home/则可以跳过登录,这显然是不合理的。

我们做一个优化,用户登录成功之后,将cookie数据发送给浏览器,浏览器进行保存。代码如下。【补充:每个网站有自己的独有的存储cookie的地方,不会进行相互干扰,我们在下次进行访问以127.0.0.1:8000开头的url时,浏览器会自动把对应的cookie以字典(k:v键值对)的方式发送给后端,所以,我们可以使用cookie来做一个登录认证】

def login(request):
    # 如果是post请求
    if request.method == 'POST':
        # 拿到前端提交的数据
        username = request.POST.get('username')
        password = request.POST.get('password')
        # 进行一个简单的逻辑判断【正式的情况下,使用数据库】
        if username == 'yangyi' and password == '123':
            # 如果登录登录,我们让浏览器记录cookie数据
            obj = redirect('/home/')
            obj.set_cookie('username', 'yangyi215')  # 设置cookie
            # 重定向到home页面
            return obj
    return render(request, 'login.html')

2、基于cookie实现的登录校验

我们现在使用cookie来实现登录校验,要求如下:

1、用户登录之后,跳转到home页面
2、如果用户在没有登陆的情况下,在url中访问其他页面,自动跳转到登陆页面进行登录。
3、到登陆页面登陆成功之后,跳转到用户一开始想要访问的页面。

那么问题来了,我们如何让在访问其他页面的判断是否登录呢?这里可以联想到python的装饰器,我们建立一个登录认证装饰器。代码如下。

# 极为重要
"""登录认证装饰器"""
# 登录认证装饰器
def login_auth(func):
    # 这里极为巧妙,将request当作参数传入
    def wrapper(request, *args, **kwargs):
        # 我们要拿到target_url来进行相关页面的跳转【先看下面】
        # target_url = request.path
        target_url = request.get_full_path()
        # 对浏览器发来的cookie数据进行校验
        # 如果正确,那么就无需进行再次登录,直接跳转到目标url即可
        if request.COOKIES.get('username') == 'yangyi215':
            res = func(request, *args, **kwargs)
            return res
        else:  # 如果没有cookie数据,就需要跳转到登录界面
            # 这里有一个小小的要求,需要在登录成功之后,跳转到目标界面,而不是默认的home揭秘那
            return redirect('/login/?next={}'.format(target_url))
    return wrapper


"""登陆界面"""
# 登录
def login(request):
    # 如果是post请求
    if request.method == 'POST':
        # 拿到前端提交的数据
        username = request.POST.get('username')
        password = request.POST.get('password')
        # 进行一个简单的逻辑判断【正式的情况下,使用数据库】
        if username == 'yangyi' and password == '123':
            # 拿到上一次要访问的页面path
            target_url = request.GET.get('next')
            # 还有对target_url进行一个判断
            if target_url:
                # 进行目标页面的跳转
                obj = redirect(target_url)
            else:
                obj = redirect('/home/')
            # 如果登录登录,我们让浏览器记录cookie数据
            obj.set_cookie('username', 'yangyi215')
            # 重定向到home页面
            return obj
    return render(request, 'login.html')


# 主页
@login_auth
def home(request):
    return HttpResponse('我是主页,需要登陆成功才可以进来呦~')


# 内容页面
@login_auth
def content(request):
    return HttpResponse('我是内容页面,需要登录成功才能访问~')


# 注销页面
@login_auth
def logout(request):
    # 跳转到登录界面
    obj = redirect('/login/')
    # 删除保存到cookie数据
    obj.delete_cookie('username')
    return obj

好了,到了这里,我们实现了要求。

下次我们便可以一直保存登录状态,无需登录,直接进入homecontent页面。

三、session简介

1、session的由来

session对基于cookie做了一定的优化,当用户登陆成功之后,服务端产生一个随机字符串(在服务端保存数据,用k:v键值对的形式),并且将随机字符串交由客户端浏览器保存。

"""服务端保存数据格式如下"""
随机字符串1:用户1相关信息
随机字符串2:用户2相关信息
随机字符串3:用户3相关信息

之后浏览器访问服务端的时候,都带着该随机字符串,如何服务端中数据库进行比对,是否有对应的随机字符串从而获取到对应的用户信息。【但是如果你拿到了截获到了该随机字符串,那么你就可以冒充当前用户,其实还是有安全隐患的,web领域没有绝对的安全

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

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

2、什么是session?

数据是保存在服务端的并且它的表现形式一般也是k:v键值对(可以有多个)

"""补充"""
# token
session虽然数据是保存在服务端的,但是禁不住数据量大
token的服务端不再保存数据
登陆成功之后,将一段用户信息进行加密处理(自己定制的加密算法)
将加密之后的结果拼接在信息后面,整体返回给浏览器保存
浏览器下次访问的时候带着该信息,服务端自动切去前面一段信息再次使用自己的加密算法
跟浏览器尾部的密文进行比对,如果比对成功,则直接登陆成功。

# jwt认证
三段信息

总结:

1.cookie就是保存在客户端浏览器上的信息
2.session就是保存在服务端上的信息
3.session是基于cookie工作的(其实大部分的保存用户状态的操作都需要使用到cookie)

四、Django操作session

session数据是保存在服务端的,给客户端返回的是一个随机字符串,形式如下。

sessionid:随机字符串

  """
  session存储在服务端,可以有很多地方存储
  1.表
  2.文件
  3.缓存
  4.其他
  """

  """
  当然,有时候如果多个视图函数都需要用到一些数据的话,你一可以考虑将数据存储到django_session中,方便后续的使用。
  	eg:登录验证码
  """

1、操作session的方式

  • 增加session

    request.session['hobby'] = 'big girl'
    request.session['hobby1'] = 'small girl'
    
    # 设置过期时间
    request.session.set_expiry()
    """
    括号内可以放四种类型的参数
    1.整数          ----     多少秒
    2.日期对象      ----	   到指定日期就失效
    3.0		 ----	   一旦当前浏览器窗口关闭立刻失效
    4.不写	 ----      失效时间就取决于django内部全局session默认的失效时间(14天)
    """
    
    """
    增加session的时候内部发送了那些事?
    1.django内部会自动帮你生成一个随机字符串
    2.django内部自动将随机字符串和对应的数据存储到django_session表中
    	2.1先在内存中产生操作数据的缓存
    	2.2在响应结果django中间件的时候才真正的操作数据库
    3.将产生的随机字符串返回给客户端浏览器保存
    """
    
  • 获取session

    request.session.get("hobby")   >>> 'big girl'
    request.session.get("hobby1")  >>> 'small girl'
    
    """
    获取session的时候内部发送了那些事?
    1.自动从浏览器请求中获取sessionid对应的随机字符串
    2.拿着该随机字符串去django_session表中查找对应的数据
    3.如果比对上了,则将对应的数据取出并以字典的形式封装到request.session中
      如果比对不上 则request.session.get()返回的是None【字典的特性】
    """
    
  • 删除session

    request.session.delete()  # 只删服务端的,客户端的不删【客户端在一定条件下会自动清除】
    request.session.flush()  # 浏览器和服务端都清空(推荐使用)
    

2、session方法测试

我们在服务器中这样进行书写。

# 添加session
def set_session(request):
    # request.session是字典形式
    request.session['hobby'] = 'big girl'
    request.session['hobby1'] = 'small girl'

    return HttpResponse('添加session测试')

然后再浏览器中输入127.0.0.1:8000/set_session/,我们得到如下结果。

这是什么意思?没有django_session这张表。

因为在默认情况下操作session的时候需要django默认的一张django_session表,我们在执行数据库迁移命令的时候,django会自己创建很多表,django_session就是其中的一张。

所以我们执行数据库迁移命令,可以得到下面这些表。

表中结构如下所示。

到了这里,我们再次执行127:0.0.1:8000/set_session,我们发现表中多了django_session和浏览器中多了相关的数据。

我们在服务端中测试获取session数据,代码如下。

# 获取session
def get_session(request):
    # 如果有session数据
    if request.session:
        print(request.session)  # <django.contrib.sessions.backends.db.SessionStore object at 0x000001D2BE353DD8>
        print(request.session.get('hobby')) # big girl
        print(request.session.get('hobby1'))  # small girl
        return HttpResponse('获取session测试')
    return HttpResponse('你的酒馆对我打了烊')

另外,一个浏览器对应一条django_session表中的数据,我们使用另外的浏览器进行测试。(当session过期的时候可能会出现多条数据对应一个浏览器,但是该现象不会持续很久,内部会自动识别过期的数据清除 你也可以通过代码清除)

一个浏览器对应一条数据,主要是为了节省服务端数据库资源。

现在我们删除保留的session数据。

# 删除session
def del_session(request):
    # request.session.delete()  # 只删服务端的,客户端的不删
    request.session.flush()  # 浏览器和服务端都清空(推荐使用)
    return HttpResponse('删除')

3、基于session实现登录校验

# session版本登录认证装饰器
def login_auth(func):
    def wrapper(request, *args, **kwargs):
        # 拿到目标访问的url路径
        target_url = request.get_full_path()
        # 如果session数据符合登录要求,直接跳过登录
        # 注意:此时必须这样写,不能if request.session:因为request.session是一个对象,而不是空字典
        if request.session.get('username') == 'yangyi' and request.session.get('password') == '123':
        # if request.session:
            res = func(request, *args, **kwargs)
            return res
        else:  # 没有session数据,跳转到登录界面
            return redirect('/login/?next={}'.format(target_url))

    return wrapper


# session登录
def login(request):
    # 如果提交数据
    if request.method == 'POST':
        # 登录成功之后的跳转页面
        target_url = request.GET.get('next')
        # 拿到提交的用户名和密码
        username = request.POST.get('username')
        password = request.POST.get('password')
        # 拿后端数据进行校验
        if username == 'yangyi' and password == '123':
            # 将数据写入session中
            request.session['username'] = 'yangyi'
            request.session['password'] = '123'
            if target_url:
                return redirect(target_url)
            else:
                return redirect('/home/')
    return render(request, 'login.html')


# 主页
@login_auth
def home(request):
    return HttpResponse('我是主页,需要登陆成功才可以进来呦~')


# 内容页面
@login_auth
def content(request):
    return HttpResponse('我是内容页面,需要登录成功才能访问~')

# 注销界面
@login_auth
def logout(request):
    request.session.flush()
    return redirect('/login/')

五、CBV添加装饰器的三种方式

我们先简单一个CBV格式的数据。

from django.views import View

class MyLogin(View):
    def get(self, request):
        return HttpResponse("get请求")
    def post(self, request):
        return HttpResponse('post请求')

添加装饰器方式如下。

"""
CBV中django不建议你直接给类的方法加装饰器
无论该装饰器能都正常给你 都不建议直接加
"""

"""第一种方式"""
from django.views import View
from django.utils.decorators import method_decorator

class MyLogin(View):
    @method_decorator(login_auth)  # 方式1:指名道姓
    def get(self, request):
        return HttpResponse("get请求")
    def post(self, request):
        return HttpResponse('post请求')
    
    
"""第二种方式"""
@method_decorator(login_auth,name='get')  # 方式2(可以添加多个针对不同的方法加不同的装饰器)
@method_decorator(login_auth,name='post')
class MyLogin(View):
    def get(self, request):
        return HttpResponse("get请求")
    def post(self, request):
        return HttpResponse('post请求')
    
"""第三种方式"""
class MyLogin(View):
    @method_decorator(login_auth)   # 方式3:重写dispatch方法,它会直接作用于当前类里面的所有的方法
    def dispatch(self, request, *args, **kwargs):
        """
        通过查看CBV源码可以得出,CBV里面所有的方法在执行之前都需要先经过dispatch方法
        (该方法可以看作一个分发方法)
        """
        return super().dispatch(request,*args,**kwargs)  # 父类调用
    def get(self, request):
        return HttpResponse("get请求")
    def post(self, request):
        return HttpResponse('post请求')
posted @   YangYi215  阅读(123)  评论(0编辑  收藏  举报
编辑推荐:
· AI与.NET技术实操系列:向量存储与相似性搜索在 .NET 中的实现
· 基于Microsoft.Extensions.AI核心库实现RAG应用
· Linux系列:如何用heaptrack跟踪.NET程序的非托管内存泄露
· 开发者必知的日志记录最佳实践
· SQL Server 2025 AI相关能力初探
阅读排行:
· 震惊!C++程序真的从main开始吗?99%的程序员都答错了
· winform 绘制太阳,地球,月球 运作规律
· 【硬核科普】Trae如何「偷看」你的代码?零基础破解AI编程运行原理
· 上周热点回顾(3.3-3.9)
· 超详细:普通电脑也行Windows部署deepseek R1训练数据并当服务器共享给他人
点击右上角即可分享
微信分享提示