什么是cookie?什么是session?cookie与session有什么区别
cookie与session的由来:
网址的发展史:
1、起初网站都没有保存用户功能的需求,所有用户访问返回的结果都是一样的。
比如:新闻网页,博客网页,小说... (这些网页是不需要登录后才能访问的,每个人访问的结果都一样)
2、后来出现了一些需要保存用户信息的网站
比如:支付宝,淘宝,京东.... (用户登录后只要不长时间不访问就不会退出登录)
举例:
以登录功能为例:
# 如果不保存用户的登录状态,也就是意味着用户每次访问都需要重复的输入,用户名和密码,甚至于如果用户从该地址点击某连接,跳转到另一个子网页,也需要重复的输入用户名和密码,如果页面卡了,刷新页面也可能需要重新登录,输入用户名和密码。(这样的页面用户还会用吗?)
那么这时开发者们就想到了一个解决方案:
# 当用户第一次登录成功之后,将用户的用户名密码返回给用户浏览器,让用户浏览器保存在本地,之后用户再次访问网站的时候浏览器自动将保存在浏览器上的用户名和密码发送给服务端,服务端获取之后自动验证
# 但是早期这种方式具有非常大的安全隐患(因为这中方式是铭文保存的,完全可以被别人找到看到)
优化:
# 当用户登陆成功之后,服务端随机产生一个随机字符串(在服务端保存数据,用k:v键值对的形式),交由客户端浏览器保存。之后访问服务端的时候,都带着该随机字符串,服务端去数据库中比对是否有对应的随机字符串从而获取到对应的用户信息。
# 但是如果有人截获到了某用户的该随机字符串,那么就可以冒充他,其实也是有安全隐患的
# 在web领域是没有绝对的安全也没有绝对的不安全的。
cookie
cookie实际上是一小段的文本信息。客户端请求服务器,如果服务器需要记录该用户的状态,就使用response向客户端浏览器颁发一个cookie。客户端浏览器会把cookie保存起来。当浏览器再次请求该网站时,浏览器就会把请求地址和cookie一同给服务器。服务器检查该cookie,从而判断用户的状态。服务器还可以根据需要修改cookie的内容。
# 表现形式:
k:v键值对(可以存多个)
session
session是另一种记录客户状态的机制。不同的是cookie保存在客户端浏览器中,而session保存在服务器上。客户端浏览器访问服务器的时候,服务器把客户端信息以某种形式记录在服务器上,这就是session。客户端浏览器再次访问时只需要从该session中查找该客户的状态就可以了。 如果说cookie机制是通过检查客户身上的“通信证”,那么session机制就是通过检查服务器上的“客户明细表”来确认客户身份。
# 表现形式
数据时保存在服务端的并且他的表现形式一般也是k:v键值对(可以有多个)
1.cookie就是保存在客户端浏览器上的信息
2.session就是保存在服务端上的信息
3.session是基于cookie工作的。(大部分的保存用户状态的操作都需要使用到cookie)
token
# session虽然数据是保存在服务端的,但是禁不住数据的量大
那么token呢不需要再服务端保存数据:
用户登录成功之后呢,将一段用户信息进行加密处理(加密算法只有开发人员知道)
将加密之后的结果拼接在信息后面,整体返回给浏览器保存
浏览器下次访问的时候带着该加密信息,服务端自动会切取前面的一段真实信息再次使用自己的加密算法跟浏览器尾部的密文进行比对,比对成功则成功访问。这样就无需在服务端进行数据的保存。大大节省了存储空间。
了解了cookie与session的工作原理,接下来我们来看他们具体是怎么使用的。
cookie操作:
补充:
# 虽然cookie是服务端告诉客户端浏览器需要保存内容
# 但是如果客户端浏览器可以选择拒绝,如果客户端浏览器禁止了cookie,那么客户端浏览器就无法保存服务端发送过来的内容,那么只要是需要记录用户状态的网站登录功能都无法使用了。
比如:
# 如果在浏览器上 >> 隐私设置和安全性 >> Cookie及其他网站数据 >> 阻止了cookie
# 那么此时所有需要记录用户状态的网站的登录功能都无法使用,因为网站无法保存服务端发来的用户名密码,永远无法校验用户名和密码,这样就无法登录进去
删除当前页面的cookie:
# 我们可以在页面上手动删除我们的cookie,这样浏览器就没有记录我们的登录信息,服务端无法验证,这样就需要我们重新登录。
实操:
# 我们之前操作视图函数的返回值
return HttpResponse()
return render()
return redirect()
# 那么如果想要操作cookie就需要我们这样编写:
obj1 = HttpResponse()
# 操作cookie
return obj1
obj2 = render()
# 操作cookie
return obj2
obj3 = redirect()
# 操作cookie
return obj3
# 如果你想要操作cookie,你就不得不利用obj对象
# cookie关键字:
设置cookie:obj.set_cookie(key,value) # 需要使用到上述提到的对象
加盐处理:obj.set_signed_cookie(key,value,salt='盐')
获取cookie:request.COOKIES.get('key')
加盐数据获取:request.get_signed_cookie(key,salt='盐')
示例:
# 我们来看一个真正的登录功能
def login(request):
if request.method == 'POST': # 查询post请求数据
username = request.POST.get('username')
password = request.POST.get('password')
if username == 'gary' and password == '123':
return redirect('/home/') # 如果用户输入的匹配就跳转到home页面
return render(request,'login.html')
def home(request):
return HttpResponse('我是home页面,只有登陆的用户再能进来')
# 但是上述存在一个问题:我们需求是必须输入正确的用户名密码才可以进入home页面,但是我们直接访问/home/路由也可以直接进入home页面对吧。那么这就就需要用到cookie
def login(request):
if request.method == 'POST':
username = request.POST.get('username')
password = request.POST.get('password')
if username == 'gary' and password == '123':
**************************应用cookie区域*************************************
obj = redirect('/home/') # 用户名密码正确跳转到home主页
# 让浏览器记录cookie数据
obj.set_cookie('username','gary222') # 这里就可以随便放一个键值对,可以直接放一个用户的用户名名也可以,目的是为了让客户端浏览器保存
# 浏览器不单单的帮你存这个键值对
# 每次访问的时候还会带着他过来进行验证
return obj
*****************************************************************************
return render(request,'login.html')
def home(request):
# 获取cookie信息 判断是否有cookie
if request.COOKIES.get('username') == 'gary222': # 只有携带cookie才可以进入home页面
return HttpResponse('我是home页面,只有登陆的用户再能进来')
# 如果没有登录则跳转到登陆页面
return redirect('/login/')
# 这样我们想要直接访问home页面就不允许了,必须登录之后才能访问,并且登录之后会记录登录状态,下次再次直接访问home页面也是可以访问的。
不足之处 :
# 不足之处1:
# 现在我们只有一个home页面,那么如果有很多页面呢,是不是在视图函数很多的时候都要做一次判断,判断是否存在cookie,判断是否已经登陆,那么此时我们应该在每个也面前加一个校验用户是否已经登陆的装饰器。
# 不足之处2:
# 比如:
用户访问index页面,然后跳转到login登录页面进行登录,但是当用户登录后,此时跳转的还是home页面,这样是不合理的。需要的是用户访问什么页面,登陆后跳转的就是用户想要的页面,而不是主页面。
实现不足之处1:
# 增加验证是否登录的装饰器:
def login_auth(func): # 此时func就是home函数
def inner(request,*args,**kwargs):
if request.COOKIES.get('username'): # 判断cookie是否有值
res = func(request,*args,**kwargs) # 有值则执行对应函数
return res
else: # 没有cookie值则跳转登录页面
return redirect('/login/')
return inner
def login(request):
if request.method == 'POST':
username = request.POST.get('username')
password = request.POST.get('password')
if username == 'gary' and password == '123':
obj = redirect('/home/')
# 让浏览器记录cookie数据
obj.set_cookie('username','gary222') # 这里就可以随便放一个键值对了,可以直接放一个用户的用户名名也可以
# 浏览器不单单的帮你存这个键值对
# 每次访问的时候还会带着他过来
return obj
return render(request,'login.html')
@login_auth # 添加语法糖
def home(request):
return HttpResponse('我是home页面,只有登陆的用户再能进来')
@login_auth
def index(request):
return HttpResponse('我是index页面')
@login_auth
def shop(request):
return HttpResponse('我是shop页面')
# 此时就解决了多个页面代码冗余的问题。
实现不足之处2:
"""
用户如果在没有登陆的情况下想访问一个需要登陆的页面
那么先跳转到登陆页面 当用户输入正确的用户名和密码之后
应该跳转到用户之前想要访问的页面去 而不是直接写死
"""
# 访问登陆页面就两种情况:
要么是直接访问登陆页面的,要么是通过装饰器跳转到登录页面的
补充:获取当前用户请求的url
# print(request.path_info) # 该方法不获取路由后面的参数
# print(request.get_full_path()) # 能够获取到用户上一次想要访问的url(上一次访问的就是跳转到login页面之前想要访问的页面)(同样可获取到参数)
def login_auth(func):
def inner(request,*args,**kwargs):
target_url = request.get_full_path() # 获取用户想要访问的url
if request.COOKIES.get('username'):
res = func(request,*args,**kwargs)
return res
else:
return redirect('/login/?next=%s'%target_url)
# 这样跳转到登录页面后,url后面会携带(?next='上一次用户访问的url页面')的参数
return inner
# 那么此时就可以通过request.GET的方法拿到后面的参数,然后指定下一次跳转的页面
def login(request):
if request.method == 'POST':
username = request.POST.get('username')
password = request.POST.get('password')
if username == 'gary' and password == '123':
*****************************************************************
target_url = request.GET.get('next') # 这个结果可能是none(可能用户直接访问login)
if target_url: # 如果参数有指
obj = redirect(target_url) # 则跳转到指定路由的页面
else:
obj = redirect('/home/') # 如果用户直接访问的是登录页面则返回指定的home页面
*****************************************************************
obj.set_cookie('username','gary222')
return obj
return render(request,'login.html')
参数补充:
1、# 可以设置超时时间:cookie可以存在多长时间,过了时间就自动清除cookie,不保存登录状态则下次需要重新登录。
参数:
max_age=时间限制(以秒为单位)
expires=时间限制(以秒位单位)
两者区别:
在设置cookie的时候可以添加一个超时时间
obj.set_cookie('username', 'jason666',max_age=3,expires=3)
max_age
expires
两者都是设置超时时间的 并且都是以秒为单位
需要注意的是 针对IE浏览器需要使用expires
2、主动删除cookie(类似于退出登录/注销功能)
@login_auth # 注意退出登录也是登录后才能操作的所以需要添加装饰器
def logout(request):
obj = redirect('/login/')
obj.delete_cookie('username')
return obj
session操作:
# 设置session
request.session['key'] = value
# 获取session
request.session.get('key')
# session数据是保存在服务端的,给客户端返回的是一个随机字符串的形式。
# 不是(key:value)的形式
# 而(sessionid:随机字符串)的形式
实操:
设置session
urls.py
# 设置session
url(r'set_session',views.set_session)
views.py
def set_session(request):
request.session['hobby'] = 'girl' # 设置session给前端返回一个随机字符串
return HttpResponse('hello 小姐姐!')
# 访问:
# 上述情况是因为什么呢?
# 这是因为,上述提到session的数据是保存在服务端的,那保存到那里了呢?
所以需要给session一个保存数据的地方,在默认情况下操作session的时候需要django默认的一张django_session表。
我们是否还记得,在做数据库迁移命令的时候,会自动创建出很多我们不认识的表,那么这里就有我们需要的django_session表。
# 数据库迁移命令:
makemigrations
migrate
再次访问:
# 过期时间:
django默认的session过期时间是14天,但是也可以认为的修改它。
获取session:
def get_session(request):
print(request.session.get('hobby'))
return HttpResponse('下次再见!')
# 设置session内部发生了那些事:
1.django内部会自动帮你生成一个随机字符串
2.django内部自动将随机字符串和对应的数据村粗带django_session表中
3.将产生的随机字符串返回给客户端浏览器保存
# 获取session内部发生的那些事:
1.自动从浏览器请求中获取sessionid对应的随机字符串
2.拿着该随机字符串去django_session表中查找对应的数据
3.如果比对上了,则将对应的数据(session_data)取出并以字典的形式封装到request.session中
如果比对不上,则request.session.get()返回None
研究:如果设置多个session
def set_session(request):
request.session['hobby'] = 'girl'
request.session['hobby1'] = 'girl1'
request.session['hobby2'] = 'girl2'
request.session['hobby3'] = 'girl3'
return HttpResponse('hello 小姐姐!')
# 可同时设置多个session,但是只占用一条记录
# 并且取得时候都可以取到。
def get_session(request):
print(request.session.get('hobby'))
print(request.session.get('hobby1'))
print(request.session.get('hobby2'))
print(request.session.get('hobby3'))
return HttpResponse('下次再见!')
总结:
django_session表中数据条数是取决于浏览器的
同一个计算机上(同一个ip地址)同一个浏览器只会有一条数据有效(当session过期的时候,可能会出现多条数据对应一个浏览器,但是该现象不会持续很久,内部会自动识别过期得数据并清除,也可以手动清除或通过代码清除。)
# 这么做的目的:
主要是为了节省服务端数据库资源
设置过期时间:
# 格式:request.session.set_expiry()
括号内可以放四种类型的参数
1.整数 多少秒
2.日期对象 到指定日期就失效
3.0 一旦当前浏览器窗口关闭立刻失效
4.不写 失效时间就取决于django内部全局session默认的失效时间
清除session:
# 清除session
request.session.delete() # 只删服务端的 客户端的不删
request.session.flush() # 浏览器和服务端都清空(推荐使用)
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,默认修改之后才保存(默认)
CBV如何添加装饰器
# CBV是无法直接像给函数添加装饰器一样添加的。
from django.views import View
class MyLogin(View):
@login_auth
def get(self,request):
return HttpResponse('GET请求')
def post(self,request):
return HttpResponse('POST请求')
# 无法直接添加:报错
方式一:
# 格式:在类的方法上添加:@method_decorator(装饰器名)
from django.views import View
from django.utils.decorators import method_decorator # 需要导入该模块(模块名即为:装饰方法(见明知意))
class MyLogin(View):
@method_decorator(login_auth) # 使用装饰器
def get(self,request):
return HttpResponse('GET请求')
def post(self,request):
return HttpResponse('POST请求')
方式二:
# 在类的上方添加:@method_decorator(装饰器名,name='指定给那个方法添加')
@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请求')
方式三:
# CBV源码剖析详解地址:https://www.cnblogs.com/garyhtml/p/15955229.html
# 剖析CBV源码得知:定义一个dispatch方法添加(他会作用域当前类里面得所有的方法)
class MyLogin(View):
@method_decorator(login_auth)
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请求')