四方显神

导航

Django开发笔记(九)Django组件(上)cookie和session

中间件顾名思义,是介于request与response处理之间的一道处理过程,相对比较轻量级,并且在全局改变django的输入与输出。因为改变的是全局,所以需要谨慎实用,用不好会影响到性能。

Django的中间件的定义:

Middleware is a framework of hooks into Django’s request/response processing. It’s a light, low-level “plugin” system for globally altering Django’s input or output. MiddleWare,是 Django 请求/响应处理的钩子框架。 它是一个轻量级的、低级的“插件”系统,用于全局改变 Django 的输入或输出。
【输入指代的就是客户端像服务端django发送数据,输出指代django根据客户端要求处理数据的结果返回给客户端】

如果你想修改请求,例如被传送到view中的HttpRequest对象。 或者你想修改view返回的HttpResponse对象,这些都可以通过中间件来实现。

django框架内部声明了很多的中间件,这些中间件有着各种各种的用途,有些没有被使用,有些被默认开启使用了。而被开启使用的中间件,都是在settngs.py的MIDDLEWARE中注册使用的。

Django默认的Middleware:(在settings.py文件中定义,我们以后自定义的中间件也要在settings.py中写在这里才能被加载到)

MIDDLEWARE = [
    'django.middleware.security.SecurityMiddleware',
    'django.contrib.sessions.middleware.SessionMiddleware',
    'django.middleware.common.CommonMiddleware',
    'django.middleware.csrf.CsrfViewMiddleware',
    'django.contrib.auth.middleware.AuthenticationMiddleware',
    'django.contrib.messages.middleware.MessageMiddleware',
    'django.middleware.clickjacking.XFrameOptionsMiddleware',
]

中间件所在的位置:老员工流程图又出来了

一、自定义中间件

1.示例代码

1).定义中间件创建存放自定义中间件的文件这里选择在app01里创建mds.py文件,里面定义两个中间件MD1和MD2:

from django.utils.deprecation import MiddlewareMixin


class MD1(MiddlewareMixin):

    def process_request(self,request): # 这个process_request可一个字都不能错哈,就叫这个名字
        '''

        :param request: 请求信息对象
        :return:
        '''
        print("MD1 process_request")

    def process_response(self,request,response):
        '''

        :param request: 请求信息对象
        :param response: 视图函数返回的响应体
        :return:
        '''
        print("MD1 process_response")
        return response


class MD2(MiddlewareMixin):

    def process_request(self,request): 
        '''

        :param request: 请求信息对象
        :return:
        '''
        print("MD2 process_request")

    def process_response(self,request,response):
        '''

        :param request: 请求信息对象
        :param response: 视图函数返回的响应体
        :return:
        '''
        print("MD2 process_response")
        return response
  • process_request默认返回None,返回None,则继续执行下一个中间件的process_request;一旦返回响应体对象,则会拦截返回。
  • process_response必须有一个形参response,并return response;这是view函数返回的响应体,像接力棒一样传承给最后的客户端。

2).在settings文件中注册中间件:

 3).构建路由和视图函数:

# path('index/',index)


def index(request):

    print("执行index视图函数")

    return HttpResponse("<h1> hello </h1>")

4).启动项目,访问,后台打印:

由此我们可以看出中间件的执行顺序:

 

2.process_request

process_request默认返回None,返回None,则继续执行下一个中间件的process_request;一旦返回响应体对象,则会拦截返回。

我们刚刚写的示例request里什么都没做,现在我们做一个拦截非法IP的功能:

class MD1(MiddlewareMixin):

    def process_request(self,request): # 这个process_request可一个字都不能错哈,就叫这个名字
        '''

        :param request: 请求信息对象
        :return:
        '''

        print("MD1 process_request")

        # 非法IP识别
        visit_ip = request.META.get("REMOTE_ADDR")
        if visit_ip in ['127.0.0.1','127.0.0.2']:
            return HttpResponse("非法IP!") # 返回了响应体,拦截,原路返回
        return None

这里的拦截,就是不执行下面的中间件了,原路返回,上面的中间件是可以执行的:

3.process_response

对响应进行处理,希望不管返回什么响应,我都能在前头加一个“hi”,这么一个功能:

    def process_response(self,request,response):
        '''

        :param request: 请求信息对象
        :param response: 视图函数返回的响应体
        :return:
        '''
        print("MD1 process_response")
        response.content = b"hi"+response.content
        return response 

二、cookie与session

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

1.cookie

Cookie翻译成中文是小甜点,小饼干的意思。在HTTP中它表示服务器送给客户端浏览器的小甜点。其实Cookie是key-value结构,类似于一个python中的字典。随着服务器端的响应发送给客户端浏览器。然后客户端浏览器会把Cookie保存起来,当下一次再访问服务器时把Cookie再发送给服务器。 Cookie是由服务器创建,然后通过响应发送给客户端的一个键值对。客户端会保存Cookie,并会标注出Cookie的来源(哪个服务器的Cookie)。当客户端向服务器发出请求时会把所有这个服务器Cookie包含在请求中发送给服务器,这样服务器就可以识别客户端了!

cookie可以理解为每一个浏览器针对每一个服务器创建的key-value结构的本地存储文件

2.cookie语法与案例

cookie基本语法:

(1) 设置cookie:

res = HttpResponse(...) 或
rep = render(request, ...) 或
rep = redirect()

res.set_cookie(key,value,max_age...) # 不咋安全

res.set_signed_cookie(key,value,salt='加密盐',...) # 这种方法更安全

(2) 获取cookie: request.COOKIES  

(3) 删除cookie response.delete_cookie("cookie_key",path="/",domain=name)

简单案例:做一个登录的功能(路由代码省略)。

views.py:

from django.shortcuts import render,redirect,HttpResponse
from django.views.decorators.csrf import csrf_exempt

# Create your views here.

def index(request):
    # 判断该客户端是否登录
    is_login = request.COOKIES.get("is_login")
    if(is_login):
        return render(request, "index.html")
    else:
        return render(request,"login.html")


# @csrf_exempt
def login(request):
    if request.method == "GET":
        return render(request, "login.html")
    else:
        user = request.POST.get("user")
        pwd = request.POST.get("pwd")

        if user == "bj" and pwd == "123":
            # 登录成功,写一个小cookie
            res = HttpResponse("登录成功")
            res.set_cookie("is_login","TRUE")
            return res
            # return redirect("/app01/index") # 关于重定向的这个路径
        else:
            return render(request,"login.html")

index.html:

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

<h3>hello index</h3>

</body>
</html>

login.html:

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

<form action="" method="post">
    用户名:<input type="text" name = "user">
    密码:<input type="password" name="pwd">
    <input type="submit">
</form>

</body>
</html>

3.基于Django的session实现机制

cookie致命缺陷是不安全,将所有敏感数据交给客户端也就是浏览器,没有办法保证数据不丢失不泄露。所以我们要自己保存数据,浏览器只存一把钥匙就够了。

Django 提供对匿名会话(session)的完全支持。这个会话框架让你可以存储和取回每个站点访客任意数据。它在服务器端存储数据, 并以cookies的形式进行发送和接受数据。

session流程图:

写session的时候完成了三件事:创建一把钥匙;插入一条记录到表里;响应这把钥匙给客户端。

读session的时候完成了三件事:取钥匙;去表里匹配;匹配到了就取出来匹配到的键值对。

4.session语法使用

  • session 在服务器端,cookie 在客户端(浏览器)

 

  • session 默认被存在在服务器的一个文件里(不是内存)

 

 

  • session 的运行依赖 session id,而 session id 是存在 cookie 中的.

 

 

  • session 可以放在 文件、数据库、或内存中都可以。

 

 

  • 用户验证这种场合一般会用 session

 

# Django默认支持Session,并且默认是将Session数据存储在数据库中,即:django_session 表中。
   
# 配置 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,默认修改之后才保存(默认)

基于session实现登录案例:

表模型和路由省略。

views.py:

from django.shortcuts import render, redirect, HttpResponse
from app02.models import User


# Create your views here.

def index(request):
    # 读session
    # 1.取session_id的钥匙
    # 2.取Django_session表中找对应的session_data
    # 3.session_data.get("user_id")
    user_id = request.session.get("user_id")  # 不是很懂,这个user_id字段哪来的
    if user_id:
        # 登录成功过
        user = User.objects.get(pk=user_id)
        return render(request, "app02/index.html", {"user_name": user.name}) # 不是很懂555
    else:
        return redirect("/app02/login")

    pass


def login(request):
    if request.method == "GET":
        return render(request, "app02/login.html")

    else:
        user = request.POST.get("user")
        pwd = request.POST.get("pwd")
        try:
            user = User.objects.get(name=user, pwd=pwd)
            # 写session 就一句话,不必深究里面的实现,先阶段会用就行
            # 1.创建一个随机字符串
            # 2.将随机字符串作为session_key,将session键值对作为session_data存进数据库表django_session里
            # 3.将session_id和随机字符串组成键值对作为cookie返回给客户端
            request.session["user_id"] = user.pk

            return HttpResponse("登录成功")

        except Exception as e:
            return redirect("/app02/login")  # 这里 必须是/app02,写成app02/login会拼接成 /app02/login/app02/login

5.基于session实现的最后访问时间案例

在上面案例的基础上完善一些功能,代码先省略。待补充。

三、用户认证组件

其实就是基于session又做了一层封装,但是它只针对跟用户相关的比如说登录,注册,校验密码这些。

Django默认已经提供了认证系统Auth模块,我们认证的时候,会使用auth模块里面给我们提供的表。认证系统包含:

  • 用户管理
  • 权限
  • 用户组
  • 密码哈希系统
  • 用户登录或内容显示的表单和视图
  • 一个可插拔的后台系统 admin

1.Django用户模型类

Django认证系统中提供了用户模型类User保存用户的数据,这张表叫auth_user,是Django自带的,不是我们创建的。默认的User包含以下常见的基本字段:

字段名字段描述
username 必选。150个字符以内。 用户名可能包含字母数字,_@+ . 和-个字符。
first_name 可选(blank=True)。 少于等于30个字符。
last_name 可选(blank=True)。 少于等于30个字符。
email 可选(blank=True)。 邮箱地址。
password 必选。 密码的哈希加密串。 (Django 不保存原始密码)。 原始密码可以无限长而且可以包含任意字符。
groups Group 之间的多对多关系。
user_permissions Permission 之间的多对多关系。
is_staff 布尔值。 设置用户是否可以访问Admin 站点。
is_active 布尔值。 指示用户的账号是否激活。 它不是用来控制用户是否能够登录,而是描述一种帐号的使用状态。
is_superuser 是否是超级用户。超级用户具有所有权限。
last_login 用户最后一次登录的时间。
date_joined 账户创建的时间。 当账号创建时,默认设置为当前的date/time。

上面缺少一些字段,所以后面我们会对当前内置的用户模型进行改造,比如说它里面没有手机号字段,后面我们需要加上。

2.重要方法

Django 用户认证(Auth)组件需要导入 auth 模块

# 认证模块
from django.contrib import auth
# 对应数据库用户表,可以继承扩展
from django.contrib.auth.models import User

1)用户对象

create() # 创建一个普通用户,密码是明文的。
create_user() # 创建一个普通用户,密码是密文的。
create_superuser() # 与create_user() 相同,但是设置is_staff 和is_superuser为True。

set_password(*raw_password*)
# 设置用户的密码为给定的原始字符串,并负责密码的。 不会保存User对象。当None为raw_password时,密码将设置为一个不可用的密码。
check_password(*raw_password*)
# 如果给定的raw_password是用户的真实密码,则返回True,可以在校验用户密码时使用。

2)认证方法

auth.authenticate(username,password) 
# 将输入的密码转为密文去认证,认证成功返回用户对象,失败则返回None

3)登录和注销方法

from django.contrib import auth
# 该函数接受一个HttpRequest对象,以及一个认证了的User对象。此函数使用django的session框架给某个已认证的用户附加上session id等信息。
auth.login() 
# 该函数接受一个HttpRequest对象,无返回值。当调用该函数时,当前请求的session信息会全部清除。该用户即使没有登录,使用该函数也不会报错。
auth.logout()  

4)request.user

Django有一个默认中间件,叫做AuthenticationMiddleware,每次请求进来都会去session中去一个userid,取不到的话,赋值request.user = AnonymousUser() , 一个匿名用户对象。 当用户组件auth.login一旦执行,将userid到session中后,再有请求进入Django,将注册的userid对应的user对象赋值给request.user,即再后面的任何视图函数中都可以从request.user中取到该客户端的登录对象。

5)自定义用户表

from django.contrib.auth.models import AbstractUser

设置Auth认证模块使用的用户模型为我们自己定义的用户模型

格式:“子应用目录名.模型类名”

AUTH_USER_MODEL = 'users.User'

6)实例代码

from django.shortcuts import render

# Create your views here.

from django.shortcuts import render, redirect, HttpResponse

# 认证模块
from django.contrib import auth
# 对应数据库用户表,可以继承扩展
from django.contrib.auth.models import User


def index(request):
    # request.user:当前登录对象
    '''
    在AuthenticationMiddleware的process_request方法中做了一件事:
        1. user_id = request.session["user_id"]
        2.from django.contrib.auth.models import User
        在这张user表中找我们刚刚注册了的用户,找到了那么这个user就取到了
            if user:
                request.user = user
            else:
                request.user = AnonymousUser # 匿名用户的特点是多有的属性都为零值
    结论:在任何视图函数中都可以使用request.user,
        如果之前登录成功过即执行过auth.login(),那么request.user=登录对象
        如果之前没有登录成功过,request.user=匿名对象
    '''
    if request.user.id:
        # 不为零值即登录成功过
        return render(request, 'user_auth/index.html')
    else:
        return redirect("/user_auth/login")


def login(request):
    if request.method == "GET":
        return render(request, "user_auth/login.html")

    else:
        user = request.POST.get("user")
        pwd = request.POST.get("pwd")

        # user = User.objects.get(username=user,password=pwd) # 不能这么匹配,因为密码Django加密了匹配不上
        # authenticate会明文密码转加密再匹配,认证成功返回对象,失败返回None
        user = auth.authenticate(username=user, password=pwd)
        if user:
            # 认证成功
            # auth.login做了很多,我们只关心request.session["user_id"] = user.pk这个,不需要我们自己做了
            auth.login(request, user)
            return redirect("/user_auth/index")
        else:
            return redirect("/user_auth/login")

def logout(request):
    # request.session.flush() 这个是无脑全清
    auth.logout(request)
    return redirect("/user_auth/login")

 

posted on 2023-12-21 14:07  szdbjooo  阅读(21)  评论(0)    收藏  举报