cookie与session发展史
"""发展史1.网站都没有保存用户功能的需求 所有用户访问返回的结果都是一样的eg:新闻、博客、文章...2.出现了一些需要保存用户信息的网站eg:淘宝、支付宝、京东...以登陆功能为例:如果不保存用户登陆状态 也就意味着用户每次访问网站都需要重复的输入用户名和密码(你觉得这样的网站你还想用吗?)当用户第一次登陆成功之后 将用户的用户名密码返回给用户浏览器 让用户浏览器保存在本地,之后访问网站的时候浏览器自动将保存在浏览器上的用户名和密码发送给服务端,服务端获取之后自动验证早起这种方式具有非常大的安全隐患优化:当用户登陆成功之后,服务端产生一个随机字符串(在服务端保存数据,用kv键值对的形式),交由客户端浏览器保存随机字符串1:用户1相关信息随机字符串2:用户2相关信息随机字符串3:用户3相关信息之后访问服务端的时候,都带着该随机字符串,服务端去数据库中比对是否有对应的随机字符串从而获取到对应的用户信息但是如果你拿到了截获到了该随机字符串,那么你就可以冒充当前用户 其实还是有安全隐患的你要知道在web领域没有绝对的安全也没有绝对的不安全"""
总结:
# 总结:1.cookie就是保存在客户端浏览器上的信息2.session就是保存在服务端上的信息3.session是基于cookie工作的(其实大部分的保存用户状态的操作都需要使用到cookie)# cookie服务端保存在客户端浏览器上的信息都可以称之为cookie它的表现形式一般都是k:v键值对(可以有多个)# session数据是保存在服务端的并且它的表现形式一般也是k:v键值对(可以有多个)# 下述内容暂时了解即可 先给我搞明白最简单的cookie与session使用再说话!tokensession虽然数据是保存在服务端的 但是经不住数据量大服务端不再保存数据登陆成功之后 将一段用户信息进行加密处理(加密算法之后你公司开发知道)将加密之后的结果拼接在信息后面 整体返回给浏览器保存浏览器下次访问的时候带着该信息 服务端自动切去前面一段信息再次使用自己的加密算法跟浏览器尾部的密文进行比对jwt认证三段信息(后期会讲 结合django一起使用)
cookie
# 虽然cookie是服务端告诉客户端浏览器需要保存内容# 但是客户端浏览器可以选择拒绝保存 如果禁止了 那么 只要是需要记录用户状态的网站登陆功能都无法使用了
可操作cookie的位置:
# 视图函数的返回值return HttpResponse()return render()return redirect()obj1 = HttpResponse()# 操作cookiereturn obj1obj2 = render()# 操作cookiereturn obj2obj3 = redirect()# 操作cookiereturn obj3# 如果你想要操作cookie,你就不得不利用obj对象,利用obj对象操作cookie
cookie操作
# 设置cookieobj = HttpResponse(...)obj.set_cookie(key,value)# 或者加盐obj = render(request, ...)obj.set_signed_cookie(key,value,salt='加密盐', max_age=3)# 在设置cookie的时候添加超时时间补充obj.set_cookie('username', 'jason666',max_age=3,expires=3)max_ageexpires两者都是设置超时时间的 并且都是以秒为单位需要注意的是 针对IE浏览器需要使用expires max_age如果参数为None,这个cookie会延续到浏览器关闭为止# 获取cookierequest.COOKIES.get(key)# 或者在加盐后获取request.get_signed_cookie(key,salt='')# 参数:default: 默认值salt: 加密盐max_age: 后台控制过期时间# 主动删除cookie(注销功能)obj = HttpResponse(...)obj.delete_cookie('key')
实现一个登录功能
简单版
urls.py
from django.conf.urls import urlfrom django.contrib import adminfrom app01 import viewsurlpatterns = [url(r'^admin/', admin.site.urls),# 登录功能url(r'^login/', views.login),url(r'^home/', views.home),]urls.py
views.py
from django.shortcuts import render,HttpResponse,redirect# Create your views here.def login(request):if request.method == "POST":username = request.POST.get("username")password = request.POST.get("password")if username == "jason" and password == "111":# 保存用户登录状态obj = redirect("/home/")# 操作cookie让浏览器cookie数据obj.set_cookie("username","jason")"""浏览器不单单会帮你存而且后面每次访问你的时候还会带着它过来"""# 跳转到一个需要用户登录之后才能看的页面return objreturn render(request,"login.html")def home(request):# 获取cookie信息 判断是否登录if request.COOKIES.get("username"):return HttpResponse("我是home页面,只有登录之后才能观看")# 没有权限跳转到登录界面else:return redirect("/login/")
login.html
<!DOCTYPE html><html lang="en"><head><meta charset="UTF-8"><title>登录界面</title><!-- Bootstrap3 核心 CSS 文件 --><link href="https://cdn.staticfile.org/twitter-bootstrap/3.3.7/css/bootstrap.min.css" rel="stylesheet"><!-- jQuery文件。务必在bootstrap.min.js 之前引入 --><script src="https://cdn.staticfile.org/jquery/2.1.1/jquery.min.js"></script><!-- Bootstrap3 核心 JavaScript 文件 --><script src="https://cdn.staticfile.org/twitter-bootstrap/3.3.7/js/bootstrap.min.js"></script><!-- font-awesome.min.css图标库4.7版本 --><link href="https://cdn.bootcss.com/font-awesome/4.7.0/css/font-awesome.min.css" rel="stylesheet"></head><body><div class="container-fluid"><div class="row"><div class="col-md-8 col-md-offset-2"><form action="" method="post"><p>username:<input type="text" name="username" class="form-control"></p><p>password:<input type="password" name="password" class="form-control"></p><input type="submit" class="btn btn-info btn-block"></form></div></div></div></body></html>login.html
进阶版:如果用户在没有登录的情况下访问其他需要登录才能查看的页面,先跳转到登录页面,用户登录成功后,直接跳转到用户之前想要访问的页面
urls.py
from django.conf.urls import urlfrom django.contrib import adminfrom app01 import viewsurlpatterns = [url(r'^admin/', admin.site.urls),# 登录功能url(r'^login/', views.login),url(r'^home/', views.home),url(r'^index/', views.index),url(r'^teacher/', views.teacher),]urls.py
views.py
from django.shortcuts import render,HttpResponse,redirect# Create your views here.# 校验用户是否登录的装饰器"""用户如果在没有登陆的情况下想访问一个需要登陆的页面那么先跳转到登陆页面 当用户输入正确的用户名和密码之后应该跳转到用户之前想要访问的页面去 而不是直接写死"""def login_auth(func):def inner(request,*args,**kwargs):# print(request.path_info)# print(request.get_full_path())target_url = request.get_full_path() # 能够获取到用户上一次想要访问的urlif request.COOKIES.get("username"):return func(request,*args,**kwargs)else:return redirect("/login/?next=%s" %target_url)return innerdef login(request):if request.method == "POST":username = request.POST.get("username")password = request.POST.get("password")if username == "jason" and password == "111":# 拿到用户上一次想要访问的页面target_url = request.GET.get('next') # 这个结果可能是Noneif target_url:obj = redirect(target_url)else:obj = redirect('/home/')# 保存用户登陆状态# 操作cookie让浏览器保存cookie数据obj.set_cookie("username","jason")"""浏览器不单单会帮你存而且后面每次访问你的时候还会带着它过来"""# 跳转到一个需要用户登录之后才能看的页面return objreturn render(request,"login.html")# 注销,可以做个按钮连接放在页面@login_authdef logout(request):# 注销后跳转到首页obj = redirect("/login/")obj.delete_cookie("username")return obj@login_authdef home(request):return HttpResponse("我是home页面,只有登录之后才能观看")@login_authdef index(request):return HttpResponse("我是index页面,只有登录之后才能观看")@login_authdef teacher(request):return HttpResponse("我是teacher页面,只有登录之后才能观看")views.pyviews.py
login.html
<!DOCTYPE html><html lang="en"><head><meta charset="UTF-8"><title>登录界面</title><!-- Bootstrap3 核心 CSS 文件 --><link href="https://cdn.staticfile.org/twitter-bootstrap/3.3.7/css/bootstrap.min.css" rel="stylesheet"><!-- jQuery文件。务必在bootstrap.min.js 之前引入 --><script src="https://cdn.staticfile.org/jquery/2.1.1/jquery.min.js"></script><!-- Bootstrap3 核心 JavaScript 文件 --><script src="https://cdn.staticfile.org/twitter-bootstrap/3.3.7/js/bootstrap.min.js"></script><!-- font-awesome.min.css图标库4.7版本 --><link href="https://cdn.bootcss.com/font-awesome/4.7.0/css/font-awesome.min.css" rel="stylesheet"></head><body><div class="container-fluid"><div class="row"><div class="col-md-8 col-md-offset-2"><form action="" method="post"><p>username:<input type="text" name="username" class="form-control"></p><p>password:<input type="password" name="password" class="form-control"></p><input type="submit" class="btn btn-info btn-block"></form></div></div></div></body></html>login.html
session
# session数据是保存在服务端的(存在哪呢?),给客户端返回的是一个随机字符串sessionid:随机字符串# 在默认情况下操作session的时候需要django默认的一张django_session表数据库迁移命令django会自己创建很多表 django_session就是其中的一张 django_session表高阶用法 有时候如果多个视图函数都需要使用到一些数据的话,可以考虑将数据存储到django_session表中,方便后续的使用 eg:登录验证码(bbs作业涉及)# django默认session的过期时间是14天但是你也可以人为的修改它
操作session
# 设置sessionrequest.session['key'] = value"""设置session特点:可以设置多个k,v 并且在都将哦_session中同一个计算机上(IP地址)同一个浏览器只会有一条数据,在取的时候都可以正常的取存:request.session['hobby1'] = 'girl1'request.session['hobby2'] = 'girl2'request.session['hobby3'] = 'girl3'取:print(request.session.get("hobby1"))print(request.session.get("hobby2"))print(request.session.get("hobby3"))并且:django_session表中的数据条数是取决于浏览器的同一个计算机上(IP地址)同一个浏览器只会有一条数据生效(当session过期的时候可能会出现多条数据对应一个浏览器,但是该现象不会持续很久,内部会自动识别过期的数据清除 你也可以通过代码清除)主要是为了节省服务端数据库资源"""# 获取sessionrequest.session.get('key')# 设置过期时间request.session.set_expiry()括号内可以放四种类型的参数1.整数 多少秒2.日期对象 到指定日期就失效3.0 一旦当前浏览器窗口关闭立刻失效4.不写 失效时间就取决于django内部全局session默认的失效时间# 清除sessionrequest.session.delete() # 只删服务端的 客户端的不删request.session.flush() # 浏览器和服务端都清空(推荐使用)
设置和获取session内部发生的事
request.session['hobby'] = 'girl'"""内部发生了那些事1.django内部会自动帮你生成一个随机字符串2.django内部自动将随机字符串和对应的数据存储到django_session表中2.1先在内存中产生操作数据的缓存2.2在响应结果django中间件的时候才真正的操作数据库3.将产生的随机字符串返回给客户端浏览器保存"""request.session.get('hobby')"""内部发生了那些事1.自动从浏览器请求中获取sessionid对应的随机字符串2.拿着该随机字符串去django_session表中查找对应的数据3.如果比对上了 则将对应的数据取出并以字典的形式封装到request.session中如果比对不上 则request.session.get()返回的是None"""
补充
# session是保存在服务端的 但是session的保存位置可以有多种选择1.MySQL2.文件3.redis4.memcache...# django_session表中的数据条数是取决于浏览器的同一个计算机上(IP地址)同一个浏览器只会有一条数据生效(当session过期的时候可能会出现多条数据对应一个浏览器,但是该现象不会持续很久,内部会自动识别过期的数据清除 你也可以通过代码清除)主要是为了节省服务端数据库资源
session的其他方法:
# 获取、设置、删除Session中数据request.session['k1']request.session.get('k1',None)request.session['k1'] = 123request.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的keyrequest.session.session_key# 将所有Session失效日期小于当前日期的数据删除request.session.clear_expired()# 检查会话session的key在数据库中是否存在request.session.exists("session_key")# 删除当前会话的所有Session数据(只删数据库)request.session.delete()# 删除当前的会话数据并删除会话的Cookie(数据库和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失效策略。
django中session配置(在settings.py中配置)
1. 数据库SessionSESSION_ENGINE = 'django.contrib.sessions.backends.db' # 引擎(默认)2. 缓存SessionSESSION_ENGINE = 'django.contrib.sessions.backends.cache' # 引擎SESSION_CACHE_ALIAS = 'default' # 使用的缓存别名(默认内存缓存,也可以是memcache),此处别名依赖缓存的设置3. 文件SessionSESSION_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 SessionSESSION_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,默认修改之后才保存(默认)
示例1
urls.py
from django.conf.urls import urlfrom django.contrib import adminfrom app01 import viewsurlpatterns = [url(r'^admin/', admin.site.urls),url(r'^set_session/', views.set_session),]urls.py
views.py
from django.shortcuts import render,HttpResponse,redirect# Create your views here.def set_session(request):request.session["username"] = "jason"return HttpResponse("heiheihei")
浏览器访问:发现报错

原因:因为在默认情况下操作session的时候需要django默认的一张django_session表
解决方法:数据库迁移命令,django会自己创建很多表,django_session就是其中一张
执行完数据库迁移命令查看django_session这张表

字段解释:
session_key:键,就是session生成的随机字符串
session_data:键对应的数据
expire_date:失效时间
浏览器再次访问:


示例2:登录
urls.py
from django.conf.urls import urlfrom django.contrib import adminfrom app01 import viewsurlpatterns = [url(r'^admin/', admin.site.urls),# 登录功能url(r'^login/', views.login),url(r'^home/', views.home),url(r'^index/', views.index),url(r'^teacher/', views.teacher),url(r'^logout/', views.logout),]urls.py
views.py
from django.shortcuts import render,HttpResponse,redirect# Create your views here.# 校验用户是否登录的装饰器"""用户如果在没有登陆的情况下想访问一个需要登陆的页面那么先跳转到登陆页面 当用户输入正确的用户名和密码之后应该跳转到用户之前想要访问的页面去 而不是直接写死"""def login_auth(func):def inner(request,*args,**kwargs):# print(request.path_info)# print(request.get_full_path())target_url = request.get_full_path() # 能够获取到用户上一次想要访问的urlif request.session.get("username"):return func(request,*args,**kwargs)else:return redirect("/login/?next=%s" %target_url)return innerdef login(request):if request.method == "POST":username = request.POST.get("username")password = request.POST.get("password")if username == "jason" and password == "111":# 拿到用户上一次想要访问的页面target_url = request.GET.get('next') # 这个结果可能是Noneif target_url:passelse:# 保存用户登陆状态target_url='/home/'request.session["username"] = "jason"# 跳转到一个需要用户登录之后才能看的页面return redirect(target_url)return render(request,"login.html")# 注销@login_authdef logout(request):# 注销后跳转到首页request.session.flush()return redirect("/login/")@login_authdef home(request):return HttpResponse("我是home页面,只有登录之后才能观看")@login_authdef index(request):return HttpResponse("我是index页面,只有登录之后才能观看")@login_authdef teacher(request):return HttpResponse("我是teacher页面,只有登录之后才能观看")views.py
login.html
<!DOCTYPE html><html lang="en"><head><meta charset="UTF-8"><title>登录界面</title><!-- Bootstrap3 核心 CSS 文件 --><link href="https://cdn.staticfile.org/twitter-bootstrap/3.3.7/css/bootstrap.min.css" rel="stylesheet"><!-- jQuery文件。务必在bootstrap.min.js 之前引入 --><script src="https://cdn.staticfile.org/jquery/2.1.1/jquery.min.js"></script><!-- Bootstrap3 核心 JavaScript 文件 --><script src="https://cdn.staticfile.org/twitter-bootstrap/3.3.7/js/bootstrap.min.js"></script><!-- font-awesome.min.css图标库4.7版本 --><link href="https://cdn.bootcss.com/font-awesome/4.7.0/css/font-awesome.min.css" rel="stylesheet"></head><body><div class="container-fluid"><div class="row"><div class="col-md-8 col-md-offset-2"><form action="" method="post"><p>username:<input type="text" name="username" class="form-control"></p><p>password:<input type="password" name="password" class="form-control"></p><input type="submit" class="btn btn-info btn-block"></form></div></div></div></body></html>login.html
CBV如何添加装饰器
"""CBV中django不建议你直接给类的方法加装饰器无论该装饰器能都正常给你 都不建议直接加如果想要加需要导入method_decorator模块,用method_decorator给想要加装饰器的函数加装饰器from django.utils.decorators import method_decorator"""
案例:
from django.shortcuts import render,HttpResponse,redirect# Create your views here.# 想要加的装饰器def login_auth(func):def inner(request,*args,**kwargs):# print(request.path_info)# print(request.get_full_path())target_url = request.get_full_path() # 能够获取到用户上一次想要访问的urlif request.session.get("username"):return func(request,*args,**kwargs)else:return redirect("/login/?next=%s" %target_url)return innerdef login(request):if request.method == "POST":username = request.POST.get("username")password = request.POST.get("password")if username == "jason" and password == "111":# 拿到用户上一次想要访问的页面target_url = request.GET.get('next') # 这个结果可能是Noneif target_url:passelse:# 保存用户登陆状态target_url='/home/'request.session["username"] = "jason"# 跳转到一个需要用户登录之后才能看的页面return redirect(target_url)return render(request,"login.html")from django.views import Viewfrom django.utils.decorators import method_decorator # CBV中想要添加装饰器需要导入method_decorator# @method_decorator(login_auth,name='get') # 方式2:在CBV类上方(可以添加多个针对不同的方法加不同的装饰器)# @method_decorator(login_auth,name='post')class MyLogin(View):@method_decorator(login_auth) # 方式1:在想要添加装饰器的函数上方,指名道姓def get(self,request):return HttpResponse("get请求")# 方式3:(通过看CBV源码中dispatch方法,dispatch方法就是找request.method要执行的方法是否在CBV中,如果在就获取该方法并执行,类似于分发)# 所以我们自己定义一个dispatch方法(这个方法最后要重新返回调用父类中的dispatch方法),并给它加一个装饰器,它会直接作用于当前CBV类里面的所有的方法# @method_decorator(login_auth)# def dispatch(self, request, *args, **kwargs):# return super().dispatch(request, *args, **kwargs)def post(self,request):return HttpResponse("post请求")
