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使用再说话! token session虽然数据是保存在服务端的 但是经不住数据量大 服务端不再保存数据 登陆成功之后 将一段用户信息进行加密处理(加密算法之后你公司开发知道) 将加密之后的结果拼接在信息后面 整体返回给浏览器保存 浏览器下次访问的时候带着该信息 服务端自动切去前面一段信息再次使用自己的加密算法 跟浏览器尾部的密文进行比对 jwt认证 三段信息 (后期会讲 结合django一起使用)
cookie
# 虽然cookie是服务端告诉客户端浏览器需要保存内容 # 但是客户端浏览器可以选择拒绝保存 如果禁止了 那么 只要是需要记录用户状态的网站登陆功能都无法使用了
可操作cookie的位置:
# 视图函数的返回值 return HttpResponse() return render() return redirect() obj1 = HttpResponse() # 操作cookie return obj1 obj2 = render() # 操作cookie return obj2 obj3 = redirect() # 操作cookie return obj3 # 如果你想要操作cookie,你就不得不利用obj对象,利用obj对象操作cookie
cookie操作
# 设置cookie obj = 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_age expires 两者都是设置超时时间的 并且都是以秒为单位 需要注意的是 针对IE浏览器需要使用expires
max_age如果参数为None,这个cookie会延续到浏览器关闭为止 # 获取cookie request.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 url from django.contrib import admin from app01 import views urlpatterns = [ url(r'^admin/', admin.site.urls), # 登录功能 url(r'^login/', views.login), url(r'^home/', views.home), ]
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 obj return render(request,"login.html") def home(request): # 获取cookie信息 判断是否登录 if request.COOKIES.get("username"): return HttpResponse("我是home页面,只有登录之后才能观看") # 没有权限跳转到登录界面 else: return redirect("/login/")
<!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>
进阶版:如果用户在没有登录的情况下访问其他需要登录才能查看的页面,先跳转到登录页面,用户登录成功后,直接跳转到用户之前想要访问的页面
urls.py
from django.conf.urls import url from django.contrib import admin from app01 import views urlpatterns = [ 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), ]
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() # 能够获取到用户上一次想要访问的url if request.COOKIES.get("username"): return func(request,*args,**kwargs) else: return redirect("/login/?next=%s" %target_url) return inner def 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') # 这个结果可能是None if target_url: obj = redirect(target_url) else: obj = redirect('/home/') # 保存用户登陆状态 # 操作cookie让浏览器保存cookie数据 obj.set_cookie("username","jason") """ 浏览器不单单会帮你存 而且后面每次访问你的时候还会带着它过来 """ # 跳转到一个需要用户登录之后才能看的页面 return obj return render(request,"login.html") # 注销,可以做个按钮连接放在页面 @login_auth def logout(request): # 注销后跳转到首页 obj = redirect("/login/") obj.delete_cookie("username") return obj @login_auth def home(request): return HttpResponse("我是home页面,只有登录之后才能观看") @login_auth def index(request): return HttpResponse("我是index页面,只有登录之后才能观看") @login_auth def 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>
session
# session数据是保存在服务端的(存在哪呢?),给客户端返回的是一个随机字符串 sessionid:随机字符串 # 在默认情况下操作session的时候需要django默认的一张django_session表 数据库迁移命令 django会自己创建很多表 django_session就是其中的一张
django_session表高阶用法
有时候如果多个视图函数都需要使用到一些数据的话,可以考虑将数据存储到django_session表中,方便后续的使用
eg:登录验证码(bbs作业涉及)
# django默认session的过期时间是14天 但是你也可以人为的修改它
操作session
# 设置session request.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过期的时候可能会出现多条数据对应一个浏览器,但是该现象不会持续很久,内部会自动识别过期的数据清除 你也可以通过代码清除) 主要是为了节省服务端数据库资源 """ # 获取session request.session.get('key') # 设置过期时间 request.session.set_expiry() 括号内可以放四种类型的参数 1.整数 多少秒 2.日期对象 到指定日期就失效 3.0 一旦当前浏览器窗口关闭立刻失效 4.不写 失效时间就取决于django内部全局session默认的失效时间 # 清除session request.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.MySQL 2.文件 3.redis 4.memcache ... # django_session表中的数据条数是取决于浏览器的 同一个计算机上(IP地址)同一个浏览器只会有一条数据生效 (当session过期的时候可能会出现多条数据对应一个浏览器,但是该现象不会持续很久,内部会自动识别过期的数据清除 你也可以通过代码清除) 主要是为了节省服务端数据库资源
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(数据库和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. 数据库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,默认修改之后才保存(默认)
示例1
urls.py
from django.conf.urls import url from django.contrib import admin from app01 import views urlpatterns = [ url(r'^admin/', admin.site.urls), url(r'^set_session/', views.set_session), ]
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 url from django.contrib import admin from app01 import views urlpatterns = [ 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), ]
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() # 能够获取到用户上一次想要访问的url if request.session.get("username"): return func(request,*args,**kwargs) else: return redirect("/login/?next=%s" %target_url) return inner def 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') # 这个结果可能是None if target_url: pass else: # 保存用户登陆状态 target_url='/home/' request.session["username"] = "jason" # 跳转到一个需要用户登录之后才能看的页面 return redirect(target_url) return render(request,"login.html") # 注销 @login_auth def logout(request): # 注销后跳转到首页 request.session.flush() return redirect("/login/") @login_auth def home(request): return HttpResponse("我是home页面,只有登录之后才能观看") @login_auth def index(request): return HttpResponse("我是index页面,只有登录之后才能观看") @login_auth def teacher(request): return HttpResponse("我是teacher页面,只有登录之后才能观看")
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>
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() # 能够获取到用户上一次想要访问的url if request.session.get("username"): return func(request,*args,**kwargs) else: return redirect("/login/?next=%s" %target_url) return inner def 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') # 这个结果可能是None if target_url: pass else: # 保存用户登陆状态 target_url='/home/' request.session["username"] = "jason" # 跳转到一个需要用户登录之后才能看的页面 return redirect(target_url) return render(request,"login.html") from django.views import View from 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请求")