cookie 和session
cookie 和session
HTTP协议是无状态的,这意味这所有的客户端或者浏览器朝服务端发请求,服务端是不会记住客户端是谁,没办法保存用户的登录信息。
随之WEB的发展,出现了网上商城之类购物网站,这类网站的一个需求是记住当前用户是谁,并且需要记住用户的登录状态(避免每次请求页面都重新登录)。
为了解决这个需求,出现了很多总解决办法。其中最有效的一个办法是:当用户第一次登录成功后,将用户的登录信息(用户名和密码)返回给用户的浏览器,让浏览器将登录信息保存在本地浏览器(cookie);之后用户再次访问该网站时,浏览器会携带之前保存的该网站的登录信息,这样服务端获取登录信息之后自动登录验证。
但是这种方式存在很大的安全隐患:容易泄露用户的登录信息;后来又出现了优化的解决办法。
新的解决办法是:当用户初次登录成功后,服务端会产生一个随机字符串,该字符串保存在服务端(session),保存成键值对的形式。同时将该字符串交由客户端浏览器保存。之后再访问服务端的时候,浏览器都会带着这个随机字符串,服务端去数据库中匹配是否又这个随机字符串对应的用户信息;匹配成功则自动登录。
其实,如果截获到该随机字符串,那么就可以冒充当前用户,其实还是有安全隐患的。所以应了那句话:在web领域没有绝对的安全也没有绝对的不安全。
这就是cookie和session的出现历程。
cookie
:服务端保存在客户端浏览器上的信息,它的表现形式一般都是k:v键值对(可以有多个)。cookie就是保存在客户端浏览器上的信息session
:数据是保存在服务端的并且它的表现形式一般也是k:v键值对(可以有多个)。session就是保存在服务端上的信息- session是基于cookie工作的(其实大部分的保存用户状态的操作都需要使用到cookie)。
token
session虽然数据是保存在服务端的 但是禁不住数据量大
服务端不再保存数据
登陆成功之后 将一段用户信息进行加密处理(加密算法之后你公司开发知道)
将加密之后的结果拼接在信息后面 整体返回给浏览器保存
浏览器下次访问的时候带着该信息 服务端自动切去前面一段信息再次使用自己的加密算法
跟浏览器尾部的密文进行比对
cookie
cookie基本操作
虽然cookie是服务端告诉客户端浏览器需要保存内容,但是客户端浏览器可以选择拒绝保存;如果将浏览器设置为禁止保存服务端的cookie,那么只要是需要记录用户状态的网站登陆功能都无法使用了。
视图函数的返回有三种形式,HttpResponse ,render , redirect 三种形式 其实本质上内部返回的都是HttpResponse 对象,我们可以直接返回也是先将对象用一个变量保存下来在返回
方式一
return HttpResponse()
return render()
return direct()
方式2
obj1 = HttpResponse()
return obj1
obj2 = render()
return obj2
obj3 = redirect()
return obj3
我们感觉用这样的方式就是多此一举,但是这种方式可以使用在cookie上面,利用返回对象的方式来设置cookie
设置cookie
obj.set_cookie(key,value)
获取cookie
request.COOKIES[key]
request.COOKIES.get(key)
设置cookie超时时间
obj.set_cookie(key,value,max_age=3)
obj.set_cookie(key,value,expires=3)
"""
这两个都是设置超时时间的,都是秒为单位,expires是针对IE浏览器的
"""
删除cookie
obj.delete_cookie(key) # 删除用户浏览器上之前设置的usercookie值
cookie版本的登录
思路:# 我们完成一个真正的登陆功能,校验用户是否登陆的装饰器,用户如果在没有登陆的情下想访问一个需要登陆的页面,那么先跳转到登陆页面 当用户输入正确的用户名和密码之后
登录认证装饰器
def login_auth(func):
def inner(request,*args,**kwargs):
# print(request.path_info)
# print(request.get_full_path()) # 能够获取到用户上一次想要访问的url
target_url = request.get_full_path()
if request.COOKIES.get('username'):
return func(request,*args,**kwargs)
else:
return redirect('/login/?next=%s'%target_url)
return inner
view视图
def login(request):
if request.method == 'POST':
username = request.POST.get('username')
password = request.POST.get('password')
if username == 'jason' and password == '123':
# 获取用户上一次想要访问的url
target_url = request.GET.get('next') # 这个结果可能是None
if target_url:
obj = redirect(target_url)
else:
# 保存用户登陆状态
obj = redirect('/home/')
# 让浏览器记录cookie数据
obj.set_cookie('username', 'jason666')
"""
浏览器不单单会帮你存
而且后面每次访问你的时候还会带着它过来
"""
# 跳转到一个需要用户登陆之后才能看的页面
return obj
return render(request,'login.html')
@login_auth
def home(request):
# 获取cookie信息 判断你有没有
# if request.COOKIES.get('username') == 'jason666':
# return HttpResponse("我是home页面,只有登陆的用户才能进来哟~")
# # 没有登陆应该跳转到登陆页面
# return redirect('/login/')
return HttpResponse("我是home页面,只有登陆的用户才能进来哟~")
cookie 其他操作
# 获取cookie
request.get_signed_cookie(key, default=RAISE_ERROR, salt='', max_age=None)
参数:
default: 默认值
salt: 加密盐
max_age: 后台控制过期时间
# 设置cookie
rep.set_signed_cookie(key, value, salt='加密盐', max_age=None, ...)
参数:
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获取(不是绝对,底层抓包可以获取到也可以被覆盖)
session
Cookie虽然在一定程度上解决了“保持状态”的需求,但是由于Cookie本身最大支持4096字节,以及Cookie本身保存在客户端,可能被拦截或窃取。因此就需要有一种新的东西,它能支持更多的字节,并且他保存在服务器,有较高的安全性。这就是Session。
问题来了,基于HTTP协议的无状态特征,服务器根本就不知道访问者是“谁”。那么上述的Cookie就起到桥接的作用。
我们可以给每个客户端的Cookie分配一个唯一的id,这样用户在访问时,通过Cookie,服务器就知道来的人是“谁”。然后我们再根据不同的Cookie的id,在服务器上保存一段时间的私密资料,如“账号密码”等等。
总结而言:Cookie弥补了HTTP无状态的不足,让服务器知道来的人是“谁”;但是Cookie以文本的形式保存在本地,自身安全性较差;所以我们就通过Cookie识别不同的用户,对应的在Session里保存私密的信息以及超过4096字节的文本。
另外,上述所说的Cookie和Session其实是共通性的东西,不限于语言和框架。
session 的基本操作
django种的session是通过request对象下的session完成的,它类似与一个字典,操作方式和字典也是差不多的
- session数据是保存在服务端的(存?),给客户端返回的是一个随机字符串
sessionid:随机字符串 - 在默认情况下操作session的时候需要django默认的一张django_session表
- django默认session的过期时间是14天,可以认为修改它。
- session是保存在服务端的,但是session的保存位置可以有多种选择:数据库、文件等等
- django_session表中的数据条数是取决于浏览器的,同一个计算机上(IP地址)同一个浏览器只会有一条数据生效。主要是为了节省服务端数据库资源。
设置session
request.session['key']=value
获取session
request.session.get('key')
设置过期时间
request.session.set_expiry()
括号内可以放四种类型的参数
1.整数 多少秒
2.日期对象 到指定日期就失效
3.0 一旦当前浏览器窗口关闭立刻失效
4.不写 失效时间就取决于django内部全局session默认的失效时间
**清除session **
request.session.delete() # 只删服务端的 客户端的不删
request.session.flush() # 浏览器和服务端都清空(推荐使用)
这用于确保前面的会话数据不可以再次被用户的浏览器访问
session内部的那些事
设置session
request.session['is_login'] = 'yes'
1. django内部会自动帮你生成一个随机字符串
2. django内部自动将随机字符串和对应的数据存储到django_session表中,
先在内存中产生操作数据的缓存,在响应结果django中间件的时候才真正的操作数据库
3. 将产生的随机字符串返回给客户端浏览器保存
request.session.get('is_login')
1.自动从浏览器请求中获取sessionid对应的随机字符串
2.拿着该随机字符串去django_session表中查找对应的数据
3. 如果比对上了 则将对应的数据取出并以字典的形式封装到request.session中
如果比对不上 则request.session.get()返回的是None
session实现登录认证
登录装饰器
from functools import wraps
from django.shortcuts import redirect
def login_auth(func):
@wraps(func)
def inner(request, *args, **kwargs):
target_url = request.get_full_path() # 获取用户想要访问的url
if request.session.get('login_auth_key')
res = func(request, *args, **kwargs)
return res
else:
return redirect(f'/login/?next={target_url}') # 设置登录后跳转的页面url
return inner
视图函数:views.py
from utils.login_auth import login_auth
def login(request):
if request.method == 'POST':
if login_success:# 伪代码
target_url = request.GET.get('next') or 'index' # 登录前要访问的页面或者直接到index
request.session['login_auth_key'] = 'is_login'
return redirect(target_url)
return render(request, 'login.html', locals())
@login_auth
def logout(request):
request.session.flush()
return redirect('login')
django中的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,默认修改之后才保存(默认)
CBV如何添加装饰器
from django.views import View
from django.utils.decorators import method_decorator
"""
CBV中django不建议你直接给类的方法加装饰器
无论该装饰器能都正常给你 都不建议直接加
"""
# @method_decorator(login_auth,name='get') # 方式2(可以添加多个针对不同的方法加不同的装饰器)
# @method_decorator(login_auth,name='post')
class MyLogin(View):
@method_decorator(login_auth) # 方式3:它会直接作用于当前类里面的所有的方法
def dispatch(self, request, *args, **kwargs):
return super().dispatch(request,*args,**kwargs)
# @method_decorator(login_auth) # 方式1:指名道姓
def get(self,request):
return HttpResponse("get请求")
def post(self,request):
return HttpResponse('post请求')