• Cookie 与 Session


    大家都知道HTTP协议是无状态的。
    
    无状态的意思是每次请求都是独立的,它的执行情况和结果与前面的请求和之后的请求都无直接关系,
    它不会受前面的请求响应情况直接影响,也不会直接影响后面的请求响应情况。
    
    一句有意思的话来描述就是人生只如初见,对服务器来说,每次的请求都是全新的。
    
    状态可以理解为客户端和服务器在某次会话中产生的数据,那无状态的就以为这些数据不会被保留。
    会话中产生的数据又是我们需要保存的,也就是说要“保持状态”。因此Cookie就是在这样一个场景下诞生。
    
    Cookie具体指的是一段小信息,它是服务器发送出来存储在浏览器上的一组组键值对,
    下次访问服务器时浏览器会自动携带这些键值对,以便服务器提取有用信息。
    
    1. 由服务器让浏览器进行设置的
    2. 浏览器保存在浏览器本地
    3. 下次访问时自动携带
    
    cookie的工作原理是:由服务器产生内容,浏览器收到请求后保存在本地;当浏览器再次访问时,
    浏览器会自动带上Cookie,这样服务器就能通过Cookie的内容来判断这个是“谁”了。
    
    简单的来说:cookie就是保存在浏览器本地上的一组组键值对
    
    1. 登录(只是cookie一种应用)
    2. 保存浏览习惯 如:分页浏览器,显示数据条数(只是cookie一种应用)
    3. 简单的投票 计数(只是cookie一种应用)
    
    • 当我们关闭cookie的时,登录博客园效果

    • 查看cookie

    cookie设置

    • 获取Cookie

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

    rep = HttpResponse(...)
    rep = render(request, ...)
    
    rep.set_cookie(key,value,...)
    rep.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可以被任何url的页面访问
    - domain=None, Cookie生效的域名
    - secure=False, https传输
    - httponly=False 只能http协议传输,无法被JavaScript获取(不是绝对,底层抓包可以获取到也可以被覆盖)
    
    • 删除Cookie

    def logout(request):
        rep = redirect("/login/")
        rep.delete_cookie("user")  # 删除用户浏览器上之前设置的user的cookie值
        return rep
    

    简单的创建一个django项目,使用cookie完成用户登录状态的效验

    • login.html
    <!DOCTYPE html>
    <html lang="en">
    <head>
        <meta charset="UTF-8">
        <meta http-equiv="x-ua-compatible" content="IE=edge">
        <meta name="viewport" content="width=device-width, initial-scale=1">
        <title>登录</title>
    </head>
    <body>
    <form action="" method="post">
    {{% csrf_token %}}
        <p>
            用户名: <input type="text" name="username">
        </p>
        <p>
            密码: <input type="password" name="pwd">
        </p>
        <p>{{ error }}</p>
    
        <button>提交</button>
    </form>
    </body>
    </html>
    
    • views.py
    from django.views import View                        #导入基于类的视图模块
    from django.shortcuts import render, redirect, HttpResponse#导入响应三剑客
    
    class Login(View):                                   #登录逻辑视图函数
        def get(self, request, *args, **kwargs):         #处理get请求方法
            return render(request, 'login.html')         #如果是get请求(页面刷新等操作),还是登录页面
    
        def post(self, request, *args, **kwargs):        #处理post请求方法
            username = request.POST.get('username')      #form表单提交的账户
            pwd = request.POST.get('pwd')                #form表单提交的密码
            if username == 'gkf' and pwd == '318':       #账户密码简单校验
                url = request.GET.get('return_url')      #获取return_url,后面的路径信息
                if url:                                  #如果有路径信息
                    ret = redirect(url)                  #重定向要访问的路径
                else:
                    ret = redirect('/index/')            #如果没有,默认重定向index页面
                ret.set_cookie('is_login', '1')          #利用cookie标识登录状态
                return ret                               #响应请求
            return render(request, 'login.html', {'error': '用户名或密码错误'})#密码账户错误,重定向login.html并提示
        
    def login_required(func):                           #装饰器函数,用来判断,访客登录状态
        def inner(request, *args, **kwargs):
            is_login = request.COOKIES.get('is_login')  #获取is_login的状态值
            url = request.path_info                     #访问页面的路径信息
            if is_login != '1':                         #如果状态值不为1表示未登录
                return redirect('/login/?return_url={}'.format(url))#返会login页面,并把之前访问页面信息,拼接在路径后面
            ret = func(request, *args, **kwargs)        #如果状态值是1,执行视图函数
            return ret                                  #响应请求
        return inner
    
    @login_required                                     #判断登录状态装饰器
    def index(request):                                 #处理index路由请求的视图函数
        return HttpResponse('首页')                     #简单模拟首页
    
    @login_required                                    #判断登录状态装饰器
    def home(request):                                 #处理home路由请求的视图函数
        return HttpResponse('home')                    # #简单模拟home页    
    
    • 密码账户错误登录失败

    • 访问home也 登录成功跳转home页面

    • 直接从login页面直接登录,默认返回index页面

    Session的由来

    Cookie虽然在一定程度上解决了“保持状态”的需求,但是由于Cookie本身最大支持4096字节,
    以及Cookie本身保存在客户端,可能被拦截或窃取,因此就需要有一种新的东西,它能支持更多的字节,
    并且他保存在服务器,有较高的安全性。这就是Session。
    
    问题来了,基于HTTP协议的无状态特征,服务器根本就不知道访问者是“谁”。那么上述的Cookie就起到桥接的作用。
    
    我们可以给每个客户端的Cookie分配一个唯一的id,这样用户在访问时,通过Cookie,服务器就知道来的人是“谁”。
    然后我们再根据不同的Cookie的id,在服务器上保存一段时间的私密资料,如“账号密码”等等。
    
    总结而言:Cookie弥补了HTTP无状态的不足,让服务器知道来的人是“谁”;但是Cookie以文本的形式保存在本地,
    自身安全性较差;所以我们就通过Cookie识别不同的用户,对应的在Session里保存私密的信息以及超过4096字节的文本。
    
    另外,上述所说的Cookie和Session其实是共通性的东西,不限于语言和框架。
    
    #简单的来说,Session就是保存在服务器上一组组键值对(必须依赖cookie)
    
    #为什么要有session
    	1cookie保存在浏览器本地
    	2大小个数受到限制
    

    Django中Session相关方法

    # 获取
    request.session['k1']# request.session[key]  = value #['key']获取不到会报错,建议使用get获取,没有值返回None
    request.session.get('k1',None)# request.session.get(key)
    
    #设置
    request.session['k1'] = 123  
    request.session.setdefault('k1',123) # 存在则不设置
    
    #删除
    del request.session['k1']#删除
    request.session.delete()# 删除当前话的所有Session数据
    
    #设置值用的什么类型,判断也用什么数据类型做判断
    如:if is_login != '1':  设置的时候是字符串'1'才能这样写,cookie的值必须是字符串形式.
    
    # 所有 键、值、键值对
    request.session.keys()
    request.session.values()
    request.session.items()
    """
    老的mysql版本可以使用
    request.session.iterkeys()
    request.session.itervalues()
    request.session.iteritems()
    """
    
    
    # 会话session的key
    request.session.session_key
    
    # 将所有Session失效日期小于当前日期的数据删除
    request.session.clear_expired()
    
    # 检查会话session的key在数据库中是否存在
    request.session.exists("session_key")
    
    # 删除当前会话的所有Session数据
    request.session.delete()
      
    # 删除当前的会话数据并删除会话的Cookie。
    request.session.flush() 
        这用于确保前面的会话数据不可以再次被用户的浏览器访问
        例如,django.contrib.auth.logout() 函数中就会调用它。
    
    # 设置会话Session和Cookie的超时时间   #默认两周的超时时间
    request.session.set_expiry(value)
        * 如果value是个整数,session会在些秒数后失效。
        * 如果value是个datatime或timedelta,session就会在这个时间后失效。
        * 如果value是0,用户关闭浏览器session就会失效。
        * 如果value是None,session会依赖全局session失效策略。
    

    Session流程解析

    img

    Django中的Session配置

    #Django中默认支持Session,其内部提供了5种类型的Session供开发者使用。
    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'   # 引擎
    
    其他公用设置项:
    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实现登录验证

    from django.views import View  # 导入基于类的视图模块
    from django.shortcuts import render, redirect, HttpResponse  # 导入响应三剑客
    
    class Login(View):  # 登录逻辑视图函数
        def get(self, request, *args, **kwargs):  # 处理get请求方法
            return render(request, 'login.html')  # 如果是get请求(页面刷新等操作),还是登录页面
    
        def post(self, request, *args, **kwargs):  # 处理post请求方法
            username = request.POST.get('username')  # form表单提交的账户
            pwd = request.POST.get('pwd')  # form表单提交的密码
            if username == 'gkf' and pwd == '318':  # 账户密码简单校验
                url = request.GET.get('return_url')  # 获取return_url,后面的路径信息
                if url:  # 如果有路径信息
                    ret = redirect(url)  # 重定向要访问的路径
                else:
                    ret = redirect('/index/')  # 如果没有,默认重定向index页面
                request.session['k3'] = 888 # 利用session标识登录状态,值为888
                return ret  # 响应请求
            return render(request, 'login.html', {'error': '用户名或密码错误'})  # 密码账户错误,重定向login.html并提示
    
    
    def login_required(func):  # 装饰器函数,用来判断,访客登录状态
        def inner(request, *args, **kwargs):
            url = request.path_info  # 访问页面的路径信息
            value = request.session.get('k3')#获取状态值
            if  value != 888:  # 如果状态值不为888表示未登录
                return redirect('/login/?return_url={}'.format(url))  # 返会login页面,并把之前访问页面信息,拼接在路径后面
            ret = func(request, *args, **kwargs)  # 如果状态值是888,执行视图函数
            return ret  # 响应请求
        return inner
    
    
    @login_required  # 判断登录状态装饰器
    def index(request):  # 处理index路由请求的视图函数
        return HttpResponse('首页')  # 简单模拟首页
    
    
    @login_required  # 判断登录状态装饰器
    def home(request):  # 处理home路由请求的视图函数
        return HttpResponse('home')  # #简单模拟home页
    
    • 查看全局配置 from django.conf import global_settings

    • django 自带文本数据库修改不了时间

    作 者:郭楷丰
    声援博主:如果您觉得文章对您有帮助,可以点击文章右下角 推荐一下。您的鼓励是博主的最大动力!
    自 勉:生活,需要追求;梦想,需要坚持;生命,需要珍惜;但人生的路上,更需要坚强。带着感恩的心启程,学会爱,爱父母,爱自己,爱朋友,爱他人。
  • 相关阅读:
    yii2 gii 命令行自动生成控制器和模型
    控制器中的方法命名规范
    Vue Property or method "" is not defined on the instance but referenced during render. Make sure that this property is reactive, either in the data option, or for class-based
    IDEA插件:GsonFormat
    Spring Boot : Access denied for user ''@'localhost' (using password: NO)
    Typora添加主题
    Git基础命令图解
    Java Joda-Time 处理时间工具类(JDK1.7以上)
    Java日期工具类(基于JDK1.7版本)
    Oracle SQL Developer 连接Oracle出现【 状态: 失败 -测试失败: ORA-01017: invalid username/password; logon denied】
  • 原文地址:https://www.cnblogs.com/guokaifeng/p/11094113.html
Copyright © 2020-2023  润新知