第20天,Cookie与Session、Django的用户认证、事务-阿里云开发者社区

开发者社区> 开发与运维> 正文
登录阅读全文

第20天,Cookie与Session、Django的用户认证、事务

简介: 目录 一、Cookie与Session 1.1 概念 1.2 Django实现的cookie 1.2.1 获取Cookie 1.

目录

一、Cookie与Session
    1.1 概念
    1.2 Django实现的cookie
        1.2.1 获取Cookie
        1.2.2 设置Cookie
        1.2.3 删除Cookie
    1.3 Django实现的Session
        1.3.1 基本操作
        1.3.2 Session使用示例
        1.3.3 将登录验证写成装饰器
        1.3.4 Session存储的相关配置
    
二、Django的用户认证
    2.1 auth模块
        2.1.1 authenticate()
        2.1.2 login(HttpRequest, user)
        2.1.3 logout(request) 注销用户
        2.1.4 user对象的 is_authenticated()
    2.2 User对象
        2.2.1 is_authenticated()
        2.2.2 创建用户
        2.2.3 check_password(passwd)
        2.2.4 修改密码
        2.2.5 示例

一、Cookie与Session

1.1 概念

cookie不属于http协议范围,由于http协议无法保持状态,但实际情况,我们却又需要“保持状态”,因此cookie就是在这样一个场景下诞生。

cookie工作原理:
用户在浏览器登录后,服务端根据用户的登录信息生成键值对(也就是cookie),保存用户的个人信息,然后将键值对返回给浏览器,浏览器保存在本地。当浏览器再次访问时,就会自动将这个键值对(cookie)带上,这样服务端就能通过cookie的内容判断这个用户是谁了。

cookie虽然在一定程度上解决了保持状态的需求,但由于cookie本身最大支持4096字节的数据,以及cookie本身是明文保存在客户端,很容易被拦截或窃取。因此就需要一种能够支持保存更多数据,并且保存在服务端,且具有较高安全性的机制,这就是session

session工作原理:
session是基于cookie工作的。

  1. 用户登录后,服务端随机生成一个字符串,将这个随机字符串作为键值对(cookie)的value,而键值对的key则是由Django的session配置中自定义,默认情况下是sessionid,组成的cookie就是{'sessionid':'xxxxxxxxxx'},服务端将这个cookie返回给浏览器,这样,浏览器本地只保存着一个随机字符串;
  2. 服务端再将第1步中生成的随机字符串做为key,由用户信息产生的字典做为value,组成一个键值对,保存在服务器端;假设随机字符串是"abc123",那组成的键值对理论上类似这样:{'123abc':{'login':true,'username':'alex'}}
  3. 下次用户访问时,浏览器带着cookie,服务端根据cookie中的随机字符串就可以找到对应的用户信息了。

session的好处:浏览器端只存在一个随机字符串,避免用户信息被直接暴露在外。

注意:cookie和session是所有语言共通的,不限于语言和框架。

1.2 Django实现的cookie

1.2.1 获取Cookie

request.COOKIES['key']
request.get_signed_cookie(key, default=RAISE_ERROR, salt='', max_age=None)
    参数:
        default: 默认值
           salt: 加密盐
        max_age: 后台控制过期时间

1.2.2 设置Cookie

request.set_cookie(key,value,...)
request.set_signed_cookie(key,value,salt='加密盐',...)
# 参数:
        key,              键
        value='',         值
        max_age=None,     超时时间
        expires=None,     超时时间(IE requires expires, so set it if hasn't been already.)
     path='/',         Cookie生效的路径,浏览器只会把cookie回传给带有该路径的页面,这样可以避免将cookie传给站点中的其他的应用。/ 表示根路径,特殊的:根路径的cookie可以被任何url的页面访问
             
         domain=None,      Cookie生效的域名,你可用这个参数来构造一个跨站cookie。如, domain=".example.com"所构造的cookie对下面这些站点都是可读的:www.example.com 、 www2.example.com 和an.other.sub.domain.example.com 。如果该参数设置为 None ,cookie只能由设置它的站点读取。

     secure=False,      如果设置为 True ,浏览器将通过HTTPS来回传cookie。
     httponly=False     只能http协议传输,无法JavaScript获取(不是绝对,底层抓包可以获取到也可以被覆盖) 

由于cookie保存在客户端的电脑上,所以,JavaScript和jquery也可以操作cookie。

<script src='/static/js/jquery.cookie.js'></script>
$.cookie("list_pager_num", 30,{ path: '/' });

1.2.3 删除Cookie

request.delete_cookie("cookie_key",path="/",domain=name)

cookie存储到客户端
优点: 数据存在在客户端,减轻服务器端的压力,提高网站的性能。
缺点: 安全性不高:在客户端机很容易被查看或破解用户会话信息

1.3 Django实现的Session

1.3.1 基本操作

1、设置Sessions值
    request.session['session_name'] ="admin"
2、获取Sessions值
    session_name = request.session["session_name"]
3、删除Sessions值
    del request.session["session_name"]
4、检测是否操作session值
     if "session_name" is request.session :
5、request.session.flush() 
    # 删除当前的会话数据并删除会话的Cookie。这用于确保前面的会话数据不可以再次被用户的浏览器访问,例如,django.contrib.auth.logout() 函数中就会调用它。

6、用户session的随机字符串
    request.session.session_key

7、将所有Session失效日期小于当前日期的数据删
    request.session.clear_expired()

8、检查 用户session的随机字符串 在数据库中是否
    request.session.exists("session_key")

9、删除当前用户的所有Session数据
    request.session.delete("session_key")

10、设置session失效期限
    request.session.set_expiry(value)
    * 如果value是个整数,session会在些秒数后失效。
    * 如果value是个datatime或timedelta,session就会在这个时间后失效。
    * 如果value是0,用户关闭浏览器session就会失效。
    * 如果value是None,session会依赖全局session失效策略。 

1.3.2 Session使用示例

views.py

def log_in(request):

    if request.method=="POST":
        username=request.POST['user']
        password=request.POST['pwd']

        user=UserInfo.objects.filter(username=username,password=password)

        if user:
            #设置session内部的字典内容
            request.session['is_login']='true'
            request.session['username']=username

            #登录成功就将url重定向到后台的url
            return redirect('/backend/')

    #登录不成功或第一访问就停留在登录页面
    return render(request,'login.html')




def backend(request):
    print(request.session,"------cookie")
    print(request.COOKIES,'-------session')
    """
    这里必须用读取字典的get()方法把is_login的value缺省设置为False,
    当用户访问backend这个url先尝试获取这个浏览器对应的session中的
    is_login的值。如果对方登录成功的话,在login里就已经把is_login
    的值修改为了True,反之这个值就是False的
    """

    is_login=request.session.get('is_login',False)
    #如果为真,就说明用户是正常登陆的
    if is_login:
        #获取字典的内容并传入页面文件
        cookie_content=request.COOKIES
        session_content=request.session

        username=request.session['username']

        return render(request,'backend.html',locals())
    else:
        """
        如果访问的时候没有携带正确的session,
        就直接被重定向url回login页面
        """
        return redirect('/login/')



def log_out(request):
    """
    直接通过request.session['is_login']回去返回的时候,
    如果is_login对应的value值不存在会导致程序异常。所以
    需要做异常处理
    """
    try:
        #删除is_login对应的value值
        del request.session['is_login']
        
        # OR---->request.session.flush() # 删除django-session表中的对应一行记录

    except KeyError:
        pass
    #点击注销之后,直接重定向回登录页面
    return redirect('/login/')

模版文件:

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

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


</body>
</html>


===================================backend.html==================

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

<h3>hello {{ username }}</h3>
<a href="/logout/">注销</a>

</body>
</html>

1.3.3 将登录验证写成装饰器

def auth(func):
    def warrper(req,*args,**kwargs):
        try:
            is_login=request.session.get('is_login',False)
            #如果不为真,就说明用户没有登陆
            if not is_login:
                return redirect('/login/')
            return func(req, *args, **kwargs)
        except Exception as e:
            return redirect('/login/')
    return warrper

1.3.4 Session存储的相关配置

(1)存储在数据库(默认):

Django默认支持Session,并且默认是将Session数据存储在数据库中,即:django_session 表中。
  
a. 配置 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过期,SESSION_SAVE_EVERY_REQUEST和SESSION_EXPIRE_AT_BROWSER_CLOSE都必须为True

(2)存储在缓存:

a. 配置 settings.py
  
    SESSION_ENGINE = 'django.contrib.sessions.backends.cache'  # 引擎
    SESSION_CACHE_ALIAS = 'default'                            # 使用的缓存别名(默认内存缓存,也可以是memcache),此处别名依赖缓存的设置
  
  
    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,默认修改之后才保存

(3)存储在文件:

a. 配置 settings.py
  
    SESSION_ENGINE = 'django.contrib.sessions.backends.file'    # 引擎
    SESSION_FILE_PATH = None                                    # 缓存文件路径,如果为None,则使用tempfile模块获取一个临时地址tempfile.gettempdir()        
    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,默认修改之后才保存

(4)存储在缓存+数据库
数据库用于做持久化,缓存用于提高效率

SESSION_ENGINE = 'django.contrib.sessions.backends.cached_db'        # 引擎

(5) 加密cookie Session

SESSION_ENGINE = 'django.contrib.sessions.backends.signed_cookies'   # 引擎

二、Django的用户认证

2.1 auth模块

导入:from django.contrib import auth
django.contrib.auth中提供了许多方法,这里主要介绍其中的三个:

2.1.1 authenticate()

提供了用户认证,即验证用户名以及密码是否正确,一般需要username password两个关键字参数;

如果认证信息有效,会返回一个 User 对象。authenticate()会在User 对象上设置一个属性标识那种认证后端认证了该用户,且该信息在后面的登录过程中是需要的。当我们试图登陆一个从数据库中直接取出来不经过authenticate()的User对象会报错的!!

user = authenticate(username='someone',password='somepassword')

2.1.2 login(HttpRequest, user)

该函数接受一个HttpRequest对象,以及一个认证了的User对象。此函数使用django的session框架给某个已认证的用户附加上session id等信息。

from django.contrib.auth import authenticate, login
   
def my_view(request):
  username = request.POST['username']
  password = request.POST['password']
  user = authenticate(username=username, password=password)
  if user is not None:
    login(request, user)
    # Redirect to a success page.
    ...
  else:
    # Return an 'invalid login' error message.
    ...

2.1.3 logout(request) 注销用户

from django.contrib.auth import logout
   
def logout_view(request):
  logout(request)
  # Redirect to a success page.

该函数接受一个HttpRequest对象,无返回值。当调用该函数时,当前请求的session信息会全部清除。该用户即使没有登录,使用该函数也不会报错。

2.1.4 user对象的 is_authenticated()

要求:

  1. 用户登陆后才能访问某些页面;
  2. 如果用户没有登录就访问该页面的话直接跳到登录页面;
  3. 用户在跳转的登陆界面中完成登陆后,自动访问跳转到之前访问的地址。

方法一:

def my_view(request):
  if not request.user.is_authenticated():
    return redirect('%s?next=%s' % (settings.LOGIN_URL, request.path))

方法二:
django已经为我们设计好了一个用于此种情况的装饰器:login_requierd()

from django.contrib.auth.decorators import login_required
      
@login_required
def my_view(request):
  ...

若用户没有登录,则会跳转到django默认的 登录URL '/accounts/login/ ' (这个值可以在settings文件中通过LOGIN_URL进行修改)。并传递 当前访问url的绝对路径 (登陆成功后,会重定向到该路径)。

2.2 User对象

User 对象属性:username, password(必填项)password用哈希算法保存到数据库

is_staff : 用户是否拥有网站的管理权限.

is_active : 是否允许用户登录, 设置为False,可以不用删除用户来禁止 用户登录

2.2.1 is_authenticated()

如果是真正的 User 对象,返回值恒为 True 。 用于检查用户是否已经通过了认证。

通过认证并不意味着用户拥有任何权限,甚至也不检查该用户是否处于激活状态,这只是表明用户成功的通过了认证。 这个方法很重要, 在后台用request.user.is_authenticated()判断用户是否已经登录,如果true则可以向前台展示request.user.name

2.2.2 创建用户

使用 create_user 辅助函数创建用户:

from django.contrib.auth.models import User
user = User.objects.create_user(username='',password='',email='')

2.2.3 check_password(passwd)

用户需要修改密码的时候 首先要让他输入原来的密码 ,如果给定的字符串通过了密码检查,返回 True。

2.2.4 修改密码

使用 set_password() 来修改密码

user = User.objects.get(username='')
user.set_password(password='')
user.save

2.2.5 示例

注册:

def sign_up(request):
 
    state = None
    if request.method == 'POST':
 
        password = request.POST.get('password', '')
        repeat_password = request.POST.get('repeat_password', '')
        email=request.POST.get('email', '')
        username = request.POST.get('username', '')
        if User.objects.filter(username=username):
                state = 'user_exist'
        else:
                new_user = User.objects.create_user(username=username, password=password,email=email)
                new_user.save()
 
                return redirect('/book/')
    content = {
        'state': state,
        'user': None,
    }
    return render(request, 'sign_up.html', content)  

修改密码:

@login_required
def set_password(request):
    user = request.user
    state = None
    if request.method == 'POST':
        old_password = request.POST.get('old_password', '')
        new_password = request.POST.get('new_password', '')
        repeat_password = request.POST.get('repeat_password', '')
        if user.check_password(old_password):
            if not new_password:
                state = 'empty'
            elif new_password != repeat_password:
                state = 'repeat_error'
            else:
                user.set_password(new_password)
                user.save()
                return redirect("/log_in/")
        else:
            state = 'password_error'
    content = {
        'user': user,
        'state': state,
    }
    return render(request, 'set_password.html', content)

三、事务

django支持事务,当你希望一段代码是原子性操作时,就要用到事务。

什么叫原子性操作?

  • 原子性操作就是指要对一个事物进行一系列操作,而这一系列操作要么全部成功,要么 全部失败。
  • 举个例子:假设你给小明转账。你账户有10元,小明账户也有10元。你需要给小明转账5元,那么你的账户需要先减去5元,10-5=5。这时通过网络通信,给小明账户加上5元,但是偏偏这时线路出了故障。这就造成你账户的钱已经被减去,而小明账户的钱却没有加上,钱就这样不见了。我们当然不能允许这样的事情发生了。事务就能将这两个操作绑定在一起,如果第一步已经操作成功,而中途失败了,那么第一步操作也将恢复至原来的状态。

Django中实现的事务非常简单:

from django.db import transaction

with transaction.atomic():
    '''
    将需要支持事务的代码写在此处即可
    '''

版权声明:本文内容由阿里云实名注册用户自发贡献,版权归原作者所有,阿里云开发者社区不拥有其著作权,亦不承担相应法律责任。具体规则请查看《阿里云开发者社区用户服务协议》和《阿里云开发者社区知识产权保护指引》。如果您发现本社区中有涉嫌抄袭的内容,填写侵权投诉表单进行举报,一经查实,本社区将立刻删除涉嫌侵权内容。

分享:
开发与运维
使用钉钉扫一扫加入圈子
+ 订阅

集结各类场景实战经验,助你开发运维畅行无忧

其他文章
最新文章
相关文章