• 快速上手Django(七) -Django之登录cookie和session


    快速上手Django(七) -Django之登录cookie和session

    一、cookie、session基础

    会话(Session)跟踪是Web程序中常用的技术,用来跟踪用户的整个会话。常用的会话跟踪技术是Cookie与Session。Cookie通过在客户端记录信息确定用户身份,Session通过在服务器端记录信息确定用户身份。

    cookie

    Cookie原理、Set-Cookie常用字段、应用
    参考URL: https://blog.csdn.net/jiangshangchunjiezi/article/details/104433135

    在这里插入图片描述

    Cookie Http 请求头由服务器使用set-Cookie头发送,或者在Javascript中使用document.cookie设置

    Set-Cookie Http响应头 用于从服务器向用户代理发送cookie,然后浏览器会将数据设置给下次请求的Cookie请求头,返回服务器。

    session

    Session是服务端会话技术,依赖于Cookie,如果在浏览器中禁用cookie的话,那么session就失效了,因为它需要浏览器的cookie值去session里做对比。

    当用户登陆成功时,会生成一个sessionid保存在cookies中,可以在数据库django_session中查看,当用户访问其他页面时,可以通过sessionid判断用户是否已经登陆。

    二、Django Session

    Django启用Session

    Django默认启用Session,可以在设置文件settings.py

    MIDDLEWARE = [
        
        'django.contrib.sessions.middleware.SessionMiddleware',
    ]
    
    • 1
    • 2
    • 3
    • 4

    如果禁用session,则注释即可。
    禁用的话,如果你用到 request.session,就会报 ‘Request’ object has no attribute ‘session’。

    Django Session存储方式

    在settings.py文件中,可以设置session数据的存储方式,可以保存在数据库、本地缓存等。
    默认是存储在数据库中的。

    SESSION_ENGINE='django.contrib.sessions.backends.db'
    
    • 1
    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'   # 引擎
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16

    如果存储在数据库中,则需要安装Session应用,默认已设置:

    INSTALLED_APPS = [
    
        'django.contrib.sessions',
    ]
    
    • 1
    • 2
    • 3
    • 4

    在进行数据库迁移的时候,Django自动帮我们生成了django_session表,有3个字段,分别为session_key,session_data,expris_date,Django中session的默认过期时间是14天。

    迁移数据库后,就会生成django_session表:

    三、Django中自定义用户模型

    需求背景

    开启一个新项目,官方强烈推荐用户自定义用户模型,即使默认的用户模型目前已经足够,但是未来可能会要扩展。

    自定义用户模型整体实现思路

    django内置权限系统可以自定义功能扩展。

    一般情况下,我们实现自定义用户模型的时候,都会从AbstractUser继承。然后实现我们自定义的用户模型。

    整体实现思路:
    1) 自定义User模型
    2) 在settings.py中设置AUTH_USER_MODEL指向它(这样我们就告诉Django使用我们的自定义模型而不是默认模型。)
    3) 数据迁移(一定要先设置好settings后再执行migrate)
    4) Django 自定义用户登录认证
    自定义UserManager继承django的ModelBackend类,覆写authenticate方法。
    settings.py 指定你的UserManager类。

    5) 验证通过 调用django的 login方法,完成登录,此时会往django_session表入库一条记录。

    # 使用Django的login()函数进行登陆
    login(request, user)
    
    • 1
    • 2

    自定义User模型

    模型MyUser在模型User的基础上新增了上述字段,它继承父类 AbstractUser,而 AbstractUser 是模型 User的父类,因此模型MyUser具有模型User的全部字段。

    示例代码

    示例代码,具体字段根据项目所需设置

    from django.db import models
    from django.contrib.auth.models import AbstractUser
     
     
    class MyUser(AbstractUser):
        id = models.UUIDField(primary_key=True, default=uuid.uuid4, editable=False)
        name = models.CharField('姓名', max_length=50, default='匿名用户')
        introduce = models.TextField('简介', default='暂无介绍')
        company = models.CharField('公司', max_length=100, default='暂无信息')
        profession = models.CharField('职业', max_length=100, default='暂无信息')
        address = models.CharField('住址', max_length=100, default='暂无信息')
        telephone = models.CharField('电话', max_length=11, default='暂无信息')
        wx = models.CharField('微信', max_length=50, default='暂无信息')
        qq = models.CharField('QQ', max_length=50, default='暂无信息')
        wb = models.CharField('微博', max_length=100, default='暂无信息')
    	email = models.EmailField(unique=True, blank=True, null=True, verbose_name='邮箱')
        photo = models.ImageField('头像', blank=True, upload_to='images/user/')
     
        # 设置返回值
        def __str__(self):
            return self.name
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21

    继承了类 AbstractUser将 id 由自带的 int 自增改为 uuid
    修改,规定邮箱不可重复
    新建用户时间给了一个初始值
    超级用户默认为 False

    (1)settings.py配置如下

    AUTH_USER_MODEL = 'account.MyUser' # account为项目应用,MyUser为account的models.py的模型
    
    • 1

    【非必须】自定义一个管理器BaseUserManager 用于创建用户

    django自带User模型,导入方法:from django.contrib.auth.models import User
    如果你的User定义了不同的字段, 你就要去自定义一个管理器,它继承自BaseUserManager并提供两个额外的方法:

    class UserManager(BaseUserManager):
        use_in_migrations = True
     
        def _create_user(self, username, email, password, **extra_fields):
            """
            使用给定的用户名、电子邮件和密码创建并保存用户。
            """
            # 如果没有username则抛出异常
            if not username:
                raise ValueError('The given username must be set')
            # 标准化电子邮件,查看源码会发现是用@进行分割
            email = self.normalize_email(email)
            # 标准化用户名
            username = self.model.normalize_username(username)
            user = self.model(username=username, email=email, **extra_fields)
            # 为用户设置密码,将纯文本密码转换为用于数据库存储的哈希值
            user.set_password(password)
            # 保存用户
            user.save(using=self._db)
            return user
     
        def create_user(self, username, email=None, password=None, **extra_fields):
            # 设置is_staff默认值为False,is_superuser默认值为False
            extra_fields.setdefault('is_staff', False)
            extra_fields.setdefault('is_superuser', False)
            return self._create_user(username, email, password, **extra_fields)
     
        def create_superuser(self, username, email, password, **extra_fields):
            # 设置is_staff默认值为True,is_superuser默认值为True
            extra_fields.setdefault('is_staff', True)
            extra_fields.setdefault('is_superuser', True)
            
            # 如果调用此方法,is_staff必须为True,否则会抛出异常
            if extra_fields.get('is_staff') is not True:
                raise ValueError('Superuser must have is_staff=True.')
            # 如果调用此方法,is_superuser必须为True,否则会抛出异常
            if extra_fields.get('is_superuser') is not True:
                raise ValueError('Superuser must have is_superuser=True.')
     
            return self._create_user(username, email, password, **extra_fields)
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40

    我们还需要定义自己的UserManager,因为默认的UserManager在创建用户的时候使用的是username和password

    个人经验:
    但是观察这个方法 其实就是 你的用户模型入库,其实我们也没有必要 自定义BaseUserManager,在登录校验过后,我们直接使用模型自己入库即可。完全没有必要继承BaseUserManager搞一套这个东西,感觉没啥用- -!

    四、Django 自定义用户登录认证

    Django进阶教程:如何扩展Django用户模型
    参考URL: https://zhuanlan.zhihu.com/p/378856858

    需求背景描述

    有时候 Django 自带的用户登录认证不能满足我们的需求,比如我不想要用户名+密码登录,我想手机号+验证码登录,这样就需要我们去修改 Django 自带的认证了。

    django(权限、认证)系统基础

    Django的这套权限系统有三个核心,User,Permission,Group
    在Web应用中,任何的权限系统要做的第一步就是用户识别,也就是我们常说的登陆(login)。只有正确的登陆校验,知道用户是谁了,才能够知道用户能干什么,那就是许可(Permission)需要负责解决的事情,而Group则是批量设置许可的时候的一个便利手段了。

    在view中,我们就可以使用request.user获取当前的登陆用户User对象。如果当前用户没有登陆,那么request.user将会是我们之前所说的AnonymousUser对象。我们可以用User对象的is_authenticated()方法将这两者区分开来:

    if request.user.is_authenticated():
        # 做一些事情针对验证用户.
    else:
        # 做一些事情对于匿名未登录用户.
    
    • 1
    • 2
    • 3
    • 4

    那么如何登陆一个用户呢?
    需要两个函数:authenticate(username,password)和login(request,user),位于django.contrib.auth模块中;

    1.authenticate(username,password)函数需要两个参数username,password,如果校验通过则返回User对象,如果校验不通过返回None
    如果认证信息有效,会返回一个 User 对象。authenticate()会在User 对象上设置一个属性来标识后端已经认证了该用户,且该信息在后续的登录过程中是需要的。

    2.login接受两个参数,第一个是request对象,第二个是user对象。
    login方法使用SessionMiddleware将userID存入session当中。注意,在用户还未登录的时候,也存在着匿名用户的Session,在其登陆之后,之前在匿名Session中保留的信息,都会保留下来。这两个方法要结合使用,而且必须要先调用authenticate(),因为该方法会User的一个属性上纪录该用户已经通过校验了,这个属性会被随后的login过程所使用

    当用户登陆成功时,会生成一个sessionid保存在cookies中,可以在数据库django_session中查看,当用户访问其他页面时,可以通过sessionid判断用户是否已经登陆。

    实现思路

    其实这里有2种大实现方案

    【推荐】方案1:
    Django 默认使用用户认证的是ModelBackendauthenticate()方法,这个方法就是登录时对用户认证的方法。我们要改的就是这个方法。

    思路:继承ModelBackend重写authenticate方法、get_user 方法。
    认证后端是一个类,它实现了两个必要的方法get_user(user_id) 和 authenticate(request, **credentials)。

    1. get_user 方法接收一个 user_id ——可以是用户名、电话号码,或者其他唯一标识用户的信息——然后返回一个用户对象或 None。
    2. 覆写authenticate 方法 实现判断是否可以登录

    方案2:
    不用官方的那一套,但是使用的还是前面的自定义用户模型,我们自己对比,然后入库。
    设置set-cookie,然后返回。

    五、 django.contrib.auth

    django.contrib.auth应用提供了Django的认证系统与权限框架。其主要作用是:

    1. 提供用户认证服务。包括用户登录、登出及密码管理等。
    2. 提供权限系统。包括添加、删除及修改用户权限等功能。
    3. 提供用户模型User以及权限模型Permission。
    4. 集成到Django Admin后台,提供用户、组与权限的管理界面。
    5. 为其他应用(如Django REST framework)提供用户认证与权限的基础架构与接口。
      更具体来说,该应用包含:
    • 用户认证相关视图、URL与中间件。如登录、登出视图。
    • 权限相关模型(Permission)与管理器(PermissionManager)。
    • 用户模型(User)与管理器(UserManager)。
    • admin后台中的用户、组与权限管理。
    • 供其他应用使用的认证与权限系统接口(如APIView中的permission_classes)
      重要,就是你用到了permission_classes,就必须开启django.contrib.auth

    ,django.contrib.auth应用是Django内建的用户认证与权限管理方案。

    六、工作常用总结

    Django,request.user始终是匿名用户

    Django自定义后端“get_user”从未调用。用户总是匿名的
    参考URL: https://www.cnpython.com/qa/230722

    如果您使用的是自定义用户模型,那么您基本上都是自己的,而django.contrib.auth框架无法帮助您进行身份验证。

    ModelBackend类默认的实现,如下,我们看到他是从UserModel这个模型中获取的数据,而我们是自定义用户模型,所以我们也要覆写这个方法:

    def get_user(self, user_id):
        try:
            user = UserModel._default_manager.get(pk=user_id)
        except UserModel.DoesNotExist:
            return None
        return user if self.user_can_authenticate(user) else None
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6

    总结,需要确认:

    • 你setting.py 放开了 ‘django.contrib.auth.middleware.AuthenticationMiddleware’,
      我被坑在这里了,你要能调 get_user方法,你就需要开放这个中间件,get_user方法是由这个中间件,在接收到request请求时,调用的。
    • 你覆写了get_user方法。

    django 判断当前请求用户没有有登录

    is_authenticate 判断⽤户是否登录

    Django⽤户认证系统提供了⽅法request.user.is_authenticated来判断⽤户是否登录。
    如果通过登录验证则返回True。反之,返回False。
    缺点:登录验证逻辑很多地⽅都需要,所以该代码需要重复编码好多次。

    注意:ret = user.is_authenticated # is_authenticated 是属性而不是方法,所以后面没有空格!

    Django 通过中间件全局判断用户登录状态

    TODO

    参考

    一篇文章搞定 Django Cookie 与 Session
    参考URL: https://www.toutiao.com/article/6840691101863510531
    django自定义用户登录(个人笔记)
    参考URL: https://www.bbsmax.com/A/pRdBNbXndn/
    自学django中重写User模型(笔记)
    参考URL: https://blog.csdn.net/weixin_43972292/article/details/85786380
    django中request.user的由来
    参考URL: https://zhuanlan.zhihu.com/p/415424659

  • 相关阅读:
    一起看 I/O | Flutter 休闲游戏工具包发布
    【算法基础】用数组模拟栈和队列
    Map和Set
    混合云运维解决方案,支持公有云、私有云、信创云等环境
    【SpringBoot底层原理】SpringBoot底层原理实践(一)——手撕SpringBoot容器(幼儿园版)
    ProcessDB实时/时序数据库——C/C++之数据订阅
    王庆友-架构的本质:如何打造一个有序的系统?
    Git 版本控制 - Github
    使用uniapp开发时自定义tabbar
    记一个“奇葩”需求的实现
  • 原文地址:https://blog.csdn.net/inthat/article/details/127900161