cookie & session

由来

HTTP协议是无状态的,就是说浏览器客户端每次对服务端的请求都是独立的。
状态可以理解为客户端与服务端在某次会话中产生的数据,而无状态不会将这些信息保留,但是在会话中产生的数据很多情况下是需要保存的,也就是保持状态,因此COOKIE就是在这样的场景下诞生

cookie的工作原理

  • 原始cookie --- 存在安全隐患
以用户登录功能为例,当用户第一次登陆成功之后,服务端将用户的用户名和密码返回给用户的客户端浏览器,客户端浏览器将用户的登陆信息保存在本地,之后访问网址的时候浏览器会将保存在本地的用户信息自动发送给服务端,服务端获取之后与服务端保存的用户信息进行校验。
  • 优化cookie
当用户登陆之后,服务端产生一个随机字符串(在服务端以kv键值对的形式保存),返回给客户端浏览器保存
	保存方式如下
			随机字符串1:用户1相关信息
			随机字符串2:用户2相关信息
			随机字符串3:用户3相关信息
之后每次访问该服务端的时候,都带着随机字符串,服务端收到随机字符串之后就去服务端的数据库中进行比对是否有对应的随机字符串,从而获取到对应的用户信息

注意:如果随机字符串被别人截取到了,还是可以冒充当前用户,还是存在安全隐患
	 在web领域没有绝对的安全

什么是cookie

服务端保存在客户端浏览器上的信息都可以称之为cookie
在客户端浏览器cookie的表现形式一般都是k v键值对

Django中操作cookie

基本操作

  • 注意
cookie是服务端告诉客户端浏览器需要保存内容,但是如果客户端浏览器拒绝保存cookie,就会造成只要是需要记录用户状态的网站登录功能都无法使用

在视图函数中的返回值就不能直接返回下述三种情况,应该间接返回,使用变量接收返回值,然后返回变量,想要操作cookie的话就必须利用返回中间的变量
- 不使用cookie
return HttpResponse()
return render()
return redirect()

- 使用cookie
obj = HttpResponse()
操作cookie
return obj
  • 基本语法
- 当用户访问服务端时,为客户端浏览器设置cookie,客户端浏览器保存该cookie
obj.set_cookie(key,value)

- 当用户访问需要登录才能进入的页面时,服务端需要获取客户端浏览器携带的cookie,进行校验
obj.COOKIES.get(key)

登录才能访问单一页面

# 登录页面
def login(request):
    info = {'msg':''}
    if request.method == 'POST':
        username = request.POST.get('username')
        password = request.POST.get('password')
        if username == 'tank' and password == '123':
            obj = redirect('/home/')
            # max_age就是设置cookie的有效期,单位是秒,当达到了有效期后,浏览器客户端会自动删除本地的cookie信息
            obj.set_cookie('username','tank666',max_age=3)
            return obj
        else:
            info['msg'] = '帐号密码错误'

    return render(request,'login.html',locals())


# 主页
def home(request):
    if request.COOKIES.get('username') == 'tank666':
        return HttpResponse('我是home页面')
    return redirect('/login/')

多个页面都需要登录才能访问

如果多个页面都是需要登录才能访问的,还采用上述代码是有问题的
	当你打开页面,提示登录,登录后,不管登陆之前想要访问哪个页面,最后都会跳转到home页面
	应该是用户登录后直接跳转到用户上一次想要访问的页面,类似于基础阶段的装饰器
# 登录装饰器
def auth(func):
    def wrapper(request,*args,**kwargs):
        print(request.get_full_path())
        # 获取用户登录前想要访问的页面,由于用户可能本来想要访问的就是登陆页面,所以tag_url的值可能会是None
        tag_url = request.get_full_path()
        # 判断能否能够获取到cookie的键所对应的的值
        if request.COOKIES.get('username'):
            res = func(request,*args,**kwargs)
            return res
        else:
            # 跳转到login页面,是get请求,可以在?后面携带用户登录前想要访问的页面,在后端获取到用户登录前想要访问的页面
            return redirect('/login/?next=%s' % tag_url)
    return wrapper

# 登录页面
def login(request):
    info = {'msg':''}
    # 获取用户上一次想要访问的页面
    tag_url = request.GET.get('next')
    if request.method == 'POST':
        username = request.POST.get('username')
        password = request.POST.get('password')
        if username == 'tank' and password == '123':
            if tag_url:
                obj = redirect(tag_url)
            else:
                # 不能直接返回redirect,因为直接返回是无法保存cookie的,必须使用obj返回,才能记录状态,浏览器客户端才会保存
                obj = redirect('/home/')
                # max_age就是设置cookie的有效期,单位是秒,当达到了有效期后,浏览器客户端会自动删除本地的cookie信息
            obj.set_cookie('username', 'tank666',max_age=10000)
            return obj
        else:
            info['msg'] = '帐号密码错误'

    return render(request,'login.html',locals())


# 主页
@auth
def home(request):

    return HttpResponse('我是home页面')

@auth
def index(request):

    return HttpResponse('我是index页面')

@auth
def func(request):

    return HttpResponse('我是func页面')

session

由来

Cookie虽然在一定程度上解决了“保持状态”的需求,但是由于Cookie本身最大支持4096字节,以及Cookie本身保存在客户端,可能被拦截或窃取,因此就需要有一种新的东西,它能支持更多的字节,并且保存在服务器,有较高的安全性。这就是Session。

Cookie弥补了HTTP无状态的不足,让服务器知道来的人是“谁”;但是Cookie以文本的形式保存在本地,自身安全性较差;所以我们就通过Cookie识别不同的用户,对应的在Session里保存私密的信息以及超过4096字节的文本。

什么是session

数据是保存在服务端的并且它的表现形式一般也是k:v键值对(可以有多个)
session是基于cookie工作的(其实大部分的保存用户状态的操作都需要使用到cookie),因为当用户登陆之后,服务端也是会产生随机字符串(session_id)返回给客户端浏览器保存,服务端保存的是session_id & session_data等,客户端保存的是session_id:xxx

工作原理

客户端访问服务端的时候,携带session_id:xxx键值对,即cookie,客户端将session_id对应的值提交给服务端,服务端拿着值去数据库进行校验

session的基本操作

django中session的操作通过request对象下的session完成,它类似一个字典结构,操作方式和字典的操作非常类似。

  • session的数据是保存在服务端的数据库Django_session表中,给客户端返回的是一个随机字符串
  • django默认session的过期时间是14天,可以认为修改它。
  • session是保存在服务端的,但是session的保存位置可以有多种选择:数据库、文件等等
  • django_session表中的数据条数是取决于浏览器的,同一个计算机上(IP地址)同一个浏览器只会有一条数据生效。主要是为了节省服务端数据库资源。

session内部发生的事情

request.session['abc'] = 'def'

1.django内部会自动帮你生成一个随机字符串
2.django内部自动将随机字符串和对应的数据存储到django_session表中
     2.1先在内存中产生操作数据的缓存
     2.2在响应结果django中间件的时候才真正的操作数据库
3.将产生的随机字符串返回给客户端浏览器保存

request.session.get('abc',None)

1.自动从浏览器请求中获取sessionid对应的随机字符串
2.拿着该随机字符串去django_session表中查找对应的数据
3.
   如果比对上了 则将对应的数据取出并以字典的形式封装到request.session中
   如果比对不上 则request.session.get()返回的是None

session相关方法

# 获取、设置、删除Session中数据
request.session['k1']
request.session.get('k1',None)
request.session['k1'] = 123
request.session.setdefault('k1',123) # 存在则不设置
del request.session['k1']


# 所有 键、值、键值对
request.session.keys()
request.session.values()
request.session.items()
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失效策略。

CBV添加装饰器

基于类的视图函数支持三种添加装饰器的方法,这里以登录校验为例介绍三种添加装饰器的方式。

首先明确一点,CBV中django不建议你直接给类的方法加装饰器(不论装饰器是否有效)。

# 函数装饰器
def login_auth(func):
def inner(request, *args, **kwargs):
  target_url = request.get_full_path()
  
  if request.session.get('login_auth_key'):
      res = func(request, *args, **kwargs)
      return res
  else:
      return redirect(f'/login/?next={target_url}')
return inner

CBV添加装饰器方式

  • 需要先导入 from django.utils.decorators import method_decorator-
  • 在指定方法上方加@method_decorator(method_name)
  • 如果加在类上,还需要提供装饰的名字:@method_decorator(decorator_name, method_name)

方式一:给方法加装饰器

from django.views import View
from django.utils.decorators import method_decorator
 
 
class MyLogin(View):
    
    @method_decorator(login_auth)  		# 方式1:给get方法添加装饰器
    def get(self,request):
        return HttpResponse("get请求")
 
    def post(self,request):
        return HttpResponse('post请求')
    
# 优点:简单直接
# 缺点:多个方法时,需要多次添加装饰器

方式二:给类加装饰器

	
from django.views import View
from django.utils.decorators import method_decorator
 
 
@method_decorator(login_auth, name='get')  		# 同时提供装饰器和被装饰的方法
@method_decorator(login_auth, name='post')		# 支持装饰器多个叠加
class MyLogin(View):
   
    def get(self,request):
        return HttpResponse("get请求")
 
    def post(self,request):
        return HttpResponse('post请求')
 
# 优点:给不同的方法,添加不同的装饰器

方式3通过dispatch方法给的所有方法加装饰器

from django.views import View
from django.utils.decorators import method_decorator
 
 
class MyLogin(View):
    
    @method_decorator(login_auth)  			# 方式3:它会直接作用于当前类里面的所有的方法
    def dispatch(self, request, *args, **kwargs):
        return super().dispatch(request,*args,**kwargs)
   
    def get(self,request):
        return HttpResponse("get请求")
 
    def post(self,request):
        return HttpResponse('post请求')
    
# 优点:一次性给所有方法添加装饰器(某些特殊装饰器只能加在dispatch上,例如csrf认证)
# 缺点:灵活性差
posted @ 2020-06-07 19:35  微信搜索-程序媛小庄  阅读(195)  评论(0编辑  收藏  举报