关于python:Django笔记三十二之session登录验证操作

3次阅读

共计 8994 个字符,预计需要花费 23 分钟才能阅读完成。

这一篇笔记将介绍 session 相干的内容,包含如何在零碎中应用 session,以及利用 session 实现登录认证的性能。

这篇笔记将分为以下几个内容:

  1. session 的应用流程
  2. session 的配置和相干办法
  3. users 模块的筹备
  4. session 验证的的实现
  5. Session 表介绍
  6. 登录验证的几种实现模式

1、session 的应用流程

cookie 和 session 的基本概念这里不做赘述,这里简略讲一下在 Django 中如何应用自定义的模块来实现登录、登出以及仅容许登录用户拜访某些接口的操作。

Django 有一套自带的 auth 验证模块,包含用户以及用户及相应的权限的表和操作,咱们这里没有用,而是独自自定义一个 user 模块以及相应的性能函数用来实现用户的注册、登录和登出性能。

session 在这里的应用流程大抵如下:

1、通过 login 接口,验证胜利后,将某些信息写入 session,能够是 user_id,或者是某个你自定义的特定的字段,反正是后续须要进行验证是否登录胜利的数据

2、在拜访特定的、须要登录才可查看的接口前,先查看前端返回的数据中是否蕴含咱们在上一步中写入的数据来确保用户是处于登录状态,如果是,则容许持续拜访,否则返回未登录的信息,提醒用户须要先进行登录操作

3、通过 logout 接口,将用户在 login 接口里写入的登录信息抹除,返回登出胜利信息

在 Django 中,零碎主动为咱们筹备好了 session 的所有相干的操作,咱们只须要在后续的登录操作中往里面写入咱们须要验证的数据即可。

Django 这部分为咱们筹备好的 session 操作也是通过中间件的模式存在的,是 settings.py 的 MIDDLEWARE 的 'django.contrib.sessions.middleware.SessionMiddleware'

如果不指定其余存储形式,session 的数据默认存在于咱们的后端表中,这个咱们在第一次执行 migrate 的时候曾经主动为咱们创立了该表,名为 django_session

表数据的操作和查看咱们在前面再具体介绍。

2、session 的配置和相干办法

后面曾经介绍了 session 的操作流程,这里咱们介绍一下 session 的相干配置和办法。

session 配置

以下设置都在 settings.py 中设置,事实上,这些 session 的默认配置就差不多能够应用,后续有非凡需要咱们能够再来查看,这里只介绍几个我感觉不便咱们应用的。

这个中央的官网文档地址在:https://docs.djangoproject.com/zh-hans/3.2/ref/settings/#sess…

SESSION_COOKIE_AGE

session 过期工夫,默认为 1209600,即 14 24 60 * 60,为 14 天。

咱们能够在 settings.py 中配置 session 的过期时长,也能够在程序中应用办法手动配置过期时长,办法的应用咱们前面再介绍。

SESSION_COOKIE_NAME

默认值为 sessionid,在用户登录之后,申请咱们零碎,申请的 cookie 里会带上 session key-value 的参数,这个 key 就是咱们这里的 SESSION_COOKIE_NAME,默认为 sessionid。

如果想改成其余的名称间接定义即可。

SESSION_ENGING

Django 存储 session 具体数据的中央,默认值为 django.contrib.sessions.backends.db,示意存在于数据库,也就是咱们后面说的在 django_session 这张表。

也能够存储在文件或者缓存里。

session 办法

这里接着介绍一下 session 相干的办法,这些办法的调用个别是在接口里通过 request.session 来操作。

这里咱们只是做一下办法的作用和成果的介绍,具体用处咱们在之后的示例中再具体阐明。

dict 操作

咱们能够将 request.session 视作一个 dict,往里面增加 user_id,is_login 等用于标识用户是否登录的信息的时候能够间接操作,比方:

request.session["user_id"] = 1
request.session["is_login"] = True

keys()

输入 request.session.keys() 返回的就是咱们在后面往 session 里增加的数据。

同理,request.session.items() 输入的也是咱们往里增加的数据的 key-value 的值。

del 操作

当咱们应用登出操作时,能够间接应用:

del request.session["user_id"]

这种形式会删除 session 中咱们保留的 user_id 信息,这样用户在拜访咱们的接口的时候,如果咱们做登录验证的操作,就会找不到曾经登录的信息。

之前咱们说过,咱们的 session 数据会保留在数据库里,这种形式仅仅是删除 session 中某个特定的 key-value,并不会删除 django_session 表中这条数据

而如果想要间接删除这一条 session 数据,则能够应用 flush() 办法

flush()

上面的操作则会间接操作数据库删除这条 session 数据:

request.session.flush()

flush() 和 后面的 del 办法都能够用作咱们 logout 过程中的操作。

get_expiry_age()

获取 session 过期秒数,这个值就是后面咱们在 settings.py 中设置的 SESSION_COOKIE_AGE 的值。

clear_expired()

从 django_session 中移除过期的会话,上面会介绍 Session 这个 model 的相干操作,这里提前说一下这个函数。

django_session 会有一个 expire_date 字段,clear_expired() 这个操作就会删除表里 expire_date 小于以后工夫的数据。

3、users 模块的筹备

后面介绍了 session 的相干配置和办法以及 session 的根本应用流程。接下来咱们将介绍如何在零碎中应用上 session。

在介绍 session 应用前,咱们自定义一个 users application 来做一下相干筹备。

新建一个 application 和 相干的配置,在后面的笔记中都有介绍,这里不再做赘述,比方 app 的创立、在 settings.py 里 INSTALLED_APPS 里的定义,和 hunter/urls.py 的 patterns 里新建一条数据,指向 users/urls.py 等操作。

其中,在 hunter/urls.py 中对 users app 的 url 前缀咱们定义为 users,如下:

# hunter/urls.py
from django.contrib import admin
from django.urls import path, include

urlpatterns = [path('admin/', admin.site.urls),
    path('blog/', include('blog.urls')),
    path('users/', include('users.urls')),
]

咱们这里在 users/models.py 下新建一个 User model,而后对其进行相干的 migration 操作,使其表增加到数据库中。

# users/models.py

from django.db import models


class User(models.Model):
    username = models.CharField(max_length=20, verbose_name="登录用户名", unique=True)
    password = models.CharField(max_length=256, verbose_name="加密明码")

4、session 验证的的实现

接下来,咱们将新建几个接口:

  • 用户注册接口
  • 用户登录接口
  • 用户登记接口
  • 用户信息接口

能够先看下这几个接口的代码总揽,接着咱们具体介绍一下接口的操作。

users/urls.py

from django.urls import path
from users.views import LoginView, RegisterView, LogoutView, UserInfoView

urlpatterns = [path("register", RegisterView.as_view()),
    path("login", LoginView.as_view()),
    path("logout", LogoutView.as_view()),
    path("user/info", UserInfoView.as_view()),
]
users/views.py

from django.contrib.auth.hashers import make_password, check_password
from django.http import JsonResponse
from django.views import View
from users.models import User
import json


# 用户注册
class RegisterView(View):
    def post(self, request):
        request_json = json.loads(request.body)
        username = request_json.get("username")
        password = request_json.get("password")

        if not username or not password:
            result = {"code": -1, "msg": "username or password not valid"}
        else:
            if User.objects.filter(username=username).exists():
                result = {"code": -1, "msg": "username exists"}
            else:
                User.objects.create(username=username, password=make_password(password))
                result = {"code": 0, "msg": "success"}
        return JsonResponse(result, safe=False)


# 用户登录
class LoginView(View):
    def post(self, request):
        request_json = json.loads(request.body)
        username = request_json.get("username")
        password = request_json.get("password")

        if not username or not password:
            result = {"code": -1, "msg": "login info error"}
        else:
            user = User.objects.filter(username=username).first()
            if not user:
                result = {"code": -1, "msg": "username not found"}
            else:
                if check_password(password, user.password):
                    result = {"code": 0, "msg": "success"}
                    request.session["username"] = username
                else:
                    result = {"code": -1, "msg": "password error"}

        return JsonResponse(result, safe=False)


# 用户登出
class LogoutView(View):
    def post(self, request):
        if request.session.get("username"):
            del request.session["username"]
            # request.session.flush()
        return JsonResponse({"code": 0, "msg": "登出胜利"})


# 用户信息
class UserInfoView(View):
    def post(self, request):
        username = request.session.get("username")
        if username:
            result = {"code": 0, "msg": f"登录用户为 {username}"}
            status = 200
        else:
            result = {"code": -1, "msg": "用户未登录"}
            status = 401
        return JsonResponse(result, status=status)

首先介绍一下,所有申请的参数都是放在 body 里以 json 格局传递,我这里都是通过 postman 来申请测试的。

其次,在申请里,session 的解决能够间接通过 request.session 的形式进行,以下见示例。

用户注册接口

在注册接口里,这里做了参数校验的简化,间接 json.loads() 解决 body 的内容,而后通过 Django 自带的加密函数 make_password 将明码以加密的模式保留。

用户登录接口

登录接口里,首先是校验账号密码是否正确,判断正确后咱们将登录用户的 username 字段写入 session,而后在用户下一次申请的时候就会主动获取该 session。

或者更正确的来说,用户登录在操作 request.session 之后,在返回 response 的时候,零碎会在 django_session 里新增或者更新该用户的记录,这条数据有蕴含 session_key,session_data 和 expire_date 这几个字段。

session_key,在 cookie 的名称是 sessionid,postman 中第一次登录之后,在之后的每一次接口申请都会将 sessionid=xx 传给后端,后端就会依据这个 session_key 的值去 django_session 表里查问相应的记录

如果这个 session_key 在表里不存在记录,或者 expire_date 过期了,那么后端系统会主动给其值赋为 None,即认定此次接口申请是未登录状态。

expire_date 字段则是一个工夫字段,次要用于判断数据是否过期。

session_data 则是会蕴含咱们写入的数据,比方咱们在用户登录的时候,通过 request.session["username"] = username 的形式写入了一些非凡的标识,而后将其编码成 session_data 的值存入数据库,那么用户在下次申请接口的时候咱们就能够通过解码 session_data,将值取出来用于判断用户是否登录。

将 session_data 解码的形式能够独自通过获取 django_session 的记录而后获取,然而在申请中,Django 为我么做了这些解码工作,咱们能够间接通过后面介绍的 request.session.items() 的形式来查看在以后登录的 session_data 里写入的 key-value 数据。

留神: 前后端并不间接将 session_data 作为值传递,而是会传递 session_key 这个参数,一些校验的数据也都是放在 session_key 对应记录的 session_data 中存在后盾的数据库中。

用户信息接口

咱们假设获取用户信息接口要求用户必须处于登录状态,实际上也是,因为用户不登录无奈定位到用户,而后获取用户的信息。

那么咱们在进行下一步的实际操作前,咱们必定须要尝试从 session 中获取用户相应的信息,如果获取到了,则判断是处于登录状态,否则是处于未登录状态,无奈获取用户信息。

所以咱们这里的判断是从 session 中获取 username 字段,通过判断 username 是否有值来判断用户是否处于登录状态。

用户登记接口

用户登记,也就是登出接口,咱们这里用的是 del 的形式,这个次要是看咱们验证用户登录的形式,比方咱们是通过向 session 中取值 username 来判断用户是否登录,那么 del request.session["username"] 的操作即可实现登记的性能。

留神: 这里执行的 del 操作仅仅是删除 session_data 中的 {“username”: “xxx”} 的数据,这条 session_key 对应的数据还存在。

能够看到,在这条代码的下一行还有一条是执行的 flush() 操作,这个操作是间接在数据库里删除这条 session 记录,这是一种更为彻底的登出操作。

这里还须要留神的一点是,del 操作的前提是 session 数据里必须要有 username 这个 key,否则会引起报错,所以咱们这里用了一个 if 判断逻辑,咱们还能够应用 try-except 操作,或者更为彻底的操作是间接应用 flush() 操作。

至此,用户登录登出以及 session 数据的根本应用操作就介绍结束了,上面咱们额定介绍一些操作。

5、Session 表介绍

django_session 表的独自获取查看操作个别在程序里不会呈现,因为前后端都是通过 cookie 中 sessionid 间接获取到对应的数据,但为了以防万一,或者你对这张表有一些趣味,这里额定介绍一下如何独自操作这张表里的数据。

django_session 表的引入形式如下:

from django.contrib.sessions.models import Session

而后通过 session_key 来获取这条数据,比方 session_key 为 nqu3s71e38279bl5cbgju6sut64tnqmx,就能够:

session_key = "nqu3s71e38279bl5cbgju6sut64tnqmx"

session = Session.objects.get(pk=session_key)
# session = Session.objects.get(session_key=session_key)

其中,咱们向 session 里写入的数据都蕴含在 session.session_data 里,我么能够间接通过 get_decoded() 办法来获取:

session.get_decoded()

# {'username': 'root'}

6、登录验证的几种实现模式

获取用户信息这个接口须要用户登录才能够接着获取用户信息,咱们这里的操作是直接判断 session 里是否含有 username 字段。

然而如果咱们零碎里大部分接口都是须要用户先登录才可拜访,这样在每个 views 里都要先加这个判断的操作,这样的显然是不理论的。

那么咱们能够怎么操作来实现这个重复性的操作呢?

这里提供两个形式,一个是装璜器,一个是写在中间件里。

装璜器实现登录验证

其实如果间接应用 Django 自带的登录验证的性能,是能够间接应用零碎自带的装璜器的,然而咱们这里的表都是手动操作的,所以这个性能的装璜器我这里就本人实现了一个,相干代码如下:

def login_required_manual(func):
    def wrapper(*args, **kwargs):
        request = args[1]
        if not request.session.get("username"):
            return JsonResponse({"code": -1, "msg": "not login"}, status=401)
        return func(*args, **kwargs)
    return wrapper


class UserInfoView(View):
    @login_required_manual
    def post(self, request):
        username = request.session.get("username")
        return JsonResponse({"code": 0, "msg": f"登录用户 {username}"})

能够看到,应用了登录验证的装璜器之后,咱们的代码都简洁了很多。

咱们能够尝试在调用登出接口后,再调用用户信息接口,能够看到零碎就主动返回了未登录的信息了。

中间件实现登录验证

这里咱们假设目前仅仅是注册和登录不须要登录即可拜访,而后咱们创立一个中间件如下:

# hunter/middlewares/auth_middleware.py

from django.http import JsonResponse

class AuthMiddleware:

    def __init__(self, get_response):
        self.get_response = get_response

    def __call__(self, request):
        path = request.path

        # url 门路为 /users/register 和 /users/login 的接口不须要进行判断验证
        if path not in [
            "/users/register",
            "/users/login",
        ]:
            session = request.session
            if not session.get("username"):
                return JsonResponse({"code": -1, "msg": "not login"}, status=401)

        response = self.get_response(request)
        return response

而后在 hunter/settings.py 里加上这个中间件:

# hunter/settings.py

INSTALLED_APPS = [
    ...
    'hunter.middlewares.auth_middleware.AuthMiddleware',
    ...
]

这样,在每个接口申请达到 views 视图前,都会经验这个验证的中间件,这里将接口门路的判断简化成注册接口和登录接口,这两个接口不须要登录即可拜访,其余接口都设置成须要登录才可拜访。

相比于装璜器的做法,这里更举荐中间件的操作形式,这样首先就不必在每个 views 前加上装璜器,另外,须要登录才可拜访的接口都能够在中间件局部对立列举进去,不便查看。

以上就是本篇笔记对于 session 的全部内容。

本文首发于自己微信公众号:Django 笔记。

原文链接:Django 笔记三十二之 session 登录验证操作

如果想获取更多相干文章,可扫码关注浏览:

正文完
 0