一,前言
1.1,什么是会话跟踪技术
在JavaWeb中,客户向某一服务器发出第一个请求开始,会话就开始了,直到客户关闭了浏览器会话结束。在一个会话的多个请求中共享数据,这就是会话跟踪技术。
例如在一个会话中的请求如下(请求银行主页):
- 请求登录(请求参数是用户名和密码)
- 请求转账(请求参数与转账相关的数据)
- 请求信誉卡还款(请求参数与还款相关的数据)
在上面会话中,当前用户信息必须在这个会话中共享的,因为登录的是张三,那么在转账和还款时一定是相对张三的转账和还款!这就说明我们必须在一个会话过程中有共享数据的能力。
在程序中,会话跟踪是很重要的事情。理论上,一个用户的所有请求都应该属于同一个会话,而另一个用户的所有请求则该属于另一个会话,二者不能混淆。例如,用户A在超市购买的任何商品都应该放在A的购物车内,不论是用户A什么时候购买的,这都是属于同一个会话,不能放在用户B或者用户C的购物车内,这不属于同一个会话。
1.2,会话路径技术使用Cookie 或 session 完成
我们知道HTTP协议是无状态协议,也就是说每个请求都会独立的! 无法记录前一次请求的状态。但是HTTP协议中可以使用Cookie来完成会话跟踪技术!
在JavaWeb开发中,使用session来完成会话跟踪,session底层依赖Cookie技术。
- cookie就是一段字符串,保存于本机电脑上
- session保存于服务器,用来保存用户的会话信息,依赖于Cookie
cookie是通过在客户端记录信息确定用户身份,Session是通过在服务端记录信息确定用户身份。
1.3,cookie与session的实现原理
上图很明显的展示了Django的session和cookie的实现原理。服务器会生成两份相同的cookie字符串,一份保存在本地,一份发向请求的浏览器。浏览器将会受到的cookie字符串保存下来,当下次再发请求时,会将信息与这段cookie一同发送到服务器,服务器得到这段cookie会与本地保存的那份判断是否相同,如果相同就表示用户已经登录成功,保存用户登录成功的状态。Django的session保存在数据库中的数据相当于一个大字典,key为cookie的字符串,value仍是一个字典,字典的key和value为用户设置的相关信息,这样就可以方便的存取session里面的信息。
1.4,HTTP无状态性
HTTP被设计为“无状态”,每次请求都处于相同的空间中。在一次请求和下一次情况之间没有任何状态保持,我们无法根据请求的任何方面(IP地址,用户代理等)来识别同一人的连续请求,所以HTTP协议不具备保存之前发送过的请求或响应的功能。
协议对于事务处理没有记忆能力,对同一个URL请求没有上下文关系,每次的请求都是独立的,它的执行情况和结果与之前的请求和之后的请求时无直接关系的。它不会受前面的请求应答情况直接影响,也不会直接影响后面的请求应答情况。服务器中没有保存客户端的状态,客户端必须每次带上自己的状态去请求服务器。
如果说每一一种机制用来处理无状态,那么就需要花费时间不停的进行身份验证,正是这样,HTTP引入了session和cookie,即保持了HTTP的无状态性,也使得HTTP的应用称为有状态的。
二,Cookie概述
2.1,什么叫 Cookie
Cookie翻译成中文是小甜点,小饼干的意思。在HTTP中它表示服务器送给客户端浏览器的小甜点。其实Cookie是 key-value 结构,类似于一个python中的字典。随着服务器端的响应发送给客户端浏览器。然后客户端浏览器会把Cookie保存起来,当下一次再访问服务器时把Cookie再发送给服务器。
注意:cookie不能跨浏览器的,cookie中不能存在中文
Cookie是由服务器创建,然后通过响应发送给客户端的一个键值对。客户端会保存Cookie,并会标注出Cookie的来源(哪个服务器的Cookie)。当客户端向服务器发出请求时会把所有这个服务器Cookie包含在请求中发送给服务器,这样服务器就可以识别客户端!
查看cookie
我们使用Chrome浏览器,打开开发者工具。
我们举个简单的例子。
代码如下:
modes.py
from django.db import models # Create your models here. class UserInfo(models.Model): user = models.CharField(max_length=32) pwd = models.CharField(max_length=32)
我们给UserInfo表中插入一条记录,比如用户名和密码都是123(方便起见)。
views.py
from django.shortcuts import render, HttpResponse,redirect # Create your views here. from cookie_demo.models import UserInfo def index(request): if request.method == 'POST': user = request.POST.get('user') pwd = request.POST.get('pwd') user = UserInfo.objects.filter(user=user, pwd=pwd).first() if user: # 登录成功 ''' :return HttpResponse() :return render() :return redirect() 这三个都返回的是HTTPResponse,只不过render 和redirect 将其封装了 ''' response = HttpResponse("OK") response.set_cookie('is_login', True) return response else: pass return render(request, 'cookie/index.html')
index.html
index
我们输入一个正确的用户名和密码,我们在前端cookie可以看到其信息:
在响应头,我们也可以看到其信息:
2.2,HTTP的Cookie规范
- Cookie大小上限为4KB;
- 一个服务器最多在客户端浏览器上保存20个Cookie;
- 一个浏览器最多保存300个Cookie
上面的数据只是HTTP的Cookie规范,但是在浏览器大战的今天,一些浏览器为了打败对手,为了展示自己的能力起见,可能对Cookie规范“扩展”了一些,例如每个Cookie的大小为8KB,最多可保存500个Cookie等!但也不会出现将你的硬盘占满的可能!
注意:不同浏览器之间是不共享Cookie的,也就是说在你使用IE访问服务器的时候,服务器会把Cookie发给IE,然后由IE保存起来,当你在使用FireFox访问服务器时,不可能把IE保存的Cookie发送给服务器。
2.3,Cookie的用途
- 服务器使用Cookie来跟踪客户端状态
- 保存购物车
- 显示上次登录名
2.4,Cookie与HTTP头
Cookie是通过HTTP请求和响应头在客户端和服务器端传递的:
- Cookie:
请求头,客户端发送给服务器端;
格式:Cookie:a=A;b=B;c=C。即多个Cookie用分号离开;
- Set-Cookie:
响应头,服务器端发送给客户端。
一个Cookie对象一个Set-Cookie:
Set-Cookie:a=A
Set-Cookie:b = B
Set-Cookie:c=C
2.5,Cookie的覆盖
如果服务器端发送重复的Cookie,那么会覆盖原有的Cookie。
cookies是浏览器为Web服务器存储的一小段信息。每次浏览器从某个服务器请求页面时,它向服务器会送之前收到的cookies,它保存在浏览器下的某个文件夹下。
如果服务器端发送重复的Cookie那么会覆盖原有的Cookie,例如客户端的第一个请求服务器端发送的Cookie是:Set-Cookie:a=A;第二请求服务器端发送的是:Set-Cookie:a=AA,那么客户端只留下一个Cookie,即:a=AA。
2.6,Cookie 的生命周期
cookie不只有name和value,Cookie还有生命,所谓生命就是Cookie在客户端的有效时间,可以通过setMaxAge(int) 来设置Cookie的有效时间。以秒为单位。不设置默认为关闭窗口,Cookie结束。
- cookie. setMaxAge(-1):cookie的maxAge属性的默认值就是-1,表示只在浏览器内存中存活。一旦关闭浏览器窗口,那么cookie就会消失。
- cookie.setMaxAge(60*60):表示cookie对象可存活1小时。当生命大于0时,浏览器会把Cookie保存到硬盘上,就算关闭浏览器,就算重启客户端电脑,cookie也会存活1小时;
- cookie.setMaxAge(0):cookie生命等于0是一个特殊的值,它表示cookie被作废!也就是说,如果原来浏览器已经保存了这个Cookie,那么可以通过Cookie的setMaxAge(0)来删除这个Cookie。无论是在浏览器内存中,还是在客户端硬盘上都会删除这个Cookie。
过期时间
cookie 可以有过期时间,这样浏览器就知道什么时候可以删除cookie了。如果cookie没有设置过期时间,当用户关闭浏览器的时候,cookie就自动过期了。我们可以改变SESSION_EXPIRE_AT_BROWSER 的设置来控制session框架的这一行为。缺省情况下,SESSION_EXPIRE_AT_BROWSER 设置为False,这样,会话cookie可以在用户浏览器中保持有效达SESSION_COOKIE_AGE 秒(缺省设置是两周,即1209600秒)。如果你不想用户每次打开浏览器都必须重新登录的话,用这个参数来帮助你。如果SESSION_EXPIRE_AT_BROWSER_CLOSE 设置为 True ,当浏览器关闭时,Django会使cookie失效。
SESSION_COOKIE_AGE :设置cookie在浏览器中存活的时间
在settings.py中添加:
2.7,Django中的cookie语法
cookies是一种数据存储技术,是将一段文本保存在客户端(浏览器)的一种技术,并且可以长时间的保存。
1,设置cookie
rep = HttpResponse(...) 或 rep = render(request, ...) 或 rep = redirect() rep.set_cookie(key,value,...) rep.set_signed_cookie(key,value,salt='加密盐',...)
普通设置:
obj.set_cookie('tile' , 'name' , expires = value , path = '/')
加盐设置(普通cookie是明文传输的,可以直接在客户端直接打开,所以需要加盐,解盐之后才能查看):
obj.set_signed_cookie('k' , 'v' , salt = 'name' )
我们可以查看源码,发现HttpResponse() , render(),redirect() 都返回的是HttpResponse。
2,cookie参数介绍:
obj.set_cookie('tile', 'james', expires=value ,path='/', domain=None, secure=False, httponly=False)
参数:
- 1,max_age = 1 : cookie生效的时间,单位是秒
- 2,expires:具体过期日期
- 3,path = '/' : 指定哪个url 可以访问到cookie,‘/’ 表示所有,即 path = '/'
- 4,domain = None(None代表当前域名):指定哪个域名以及它下面的二级域名(子域名)可以访问这个cookie
- 5,secure = False :https安全相关
- 6,httponly = False:限制只能通过HTTP传输,JS无法在传输中获取和修改。
class HttpResponseBase: def set_cookie(self, key, #键 value = '', #值 max_age = None, #超长时间cookie需要延续的时间(以秒为单位)如果参数 #是\ None,这个cookie会延续到浏览器关闭为止。 expires = None, #超长时间,expires默认None, cookie失效的实际日期 / 时间。 path = '/', #Cookie生效的路径,浏览器只会把cookie回传给带有该路径的页面, # 这样可以避免将 cookie传给站点中的其他的应用。 # / 表示根路径,特殊的:根路径的cookie可以被任何url的页面访问 domain = None, # Cookie生效的域名你可用这个参数来构造一个跨站cookie。 # 如, domain = ".example.com"所构造的cookie对下面这些站点都是可读的: # www.example.com 、 www2.example.com和an.other.sub.domain.example.com 。 #如果该参数设置为None ,cookie只能由设置它的站点读取。secure = False, # 如果设置为True ,浏览器将通过HTTPS来 回传cookie。 httponly = False #只能http协议传输,无法被JavaScript获取 # (不是绝对,底层抓包可以获取到也可以被覆盖) ): pass
3,获取cookie
普通获取:
request.COOKIES.get('k')
加盐获取:
cookies = request.get_signed_cookie('k' , salt = 'name' )
4,删除cookie
response.delete_cookie("cookie_key",path="/",domain=name)
代码:
def logout(request): rep = redirect("/login/") # 删除用户浏览器上之前设置的user cookie的值 rep.delete_cookie("user") return rep
5,尝试给每个视图函数装饰cookie认证功能
from django.shortcuts import render,redirect,HttpResponse import datetime from until import mysqlhelper # Create your views here. def cookie_auth(func): def weaper(request, *args, **kwargs): cookies = request.get_signed_cookie('k', salt='james') if cookies == 'v': return func(request) else: return HttpResponse("NG") return weaper now = datetime.datetime.utcnow() delta = datetime.timedelta(seconds=10) def login(request): if request.method == 'GET': return render(request, 'login.html') else: username = request.POST.get('N') password = request.POST.get('P') if username == 'james' and password == '123': obj = redirect('/modal') # obj.set_cookie("tile", "james", max_age = 1,, ) value = now + delta obj.set_cookie('tile', 'james', expires=value ,path='/', domain=None, secure=False, httponly=False) obj.set_signed_cookie('k', 'v', salt='james', ) return obj else: return render(request, 'login.html') def test(request): return render(request, 'layout.html') @cookie_auth def modal(request): sql = ''' SELECT teacher.id as tid,teacher.`name`as tname,class.title FROM day64.teacher LEFT JOIN teacher_class ON day64.teacher.id=day64.teacher_class.tid LEFT JOIN day64.class ON day64.teacher_class.cid=day64.class.id; ''' teacher_list = mysqlhelper.get_list(sql, []) res = {} for row in teacher_list: tid = row['tid'] if tid in res: res[tid]['titles'].append(row['title']) else: res[tid] = {'tid':row["tid"],'tname':row["tname"],'titles':[row["title"],]} class_list=mysqlhelper.get_list("SELECT id ,title FROM day64.class" ,[]) return render(request,'modal.html',{"list":res.values(),"class_list":class_list} )
2.8,实例:保存上次访问时间
我们要做的就是显示上次访问时间。
views.py
from django.shortcuts import render, HttpResponse,redirect # Create your views here. from cookie_demo.models import UserInfo def login(request): if request.method == 'POST': user = request.POST.get('user') pwd = request.POST.get('pwd') user = UserInfo.objects.filter(user=user, pwd=pwd).first() if user: # 登录成功 response = HttpResponse("OK") response.set_cookie('is_login', True) import datetime date = datetime.datetime(year=2019, month=5, day=28, hour=9, minute=17) response.set_cookie('username', user.user,expires=date) return response else: pass return render(request, 'cookie/login.html') def index(request): print('index: ', request.COOKIES) is_login = request.COOKIES.get('is_login') if is_login: username = request.COOKIES.get('username') import datetime now = datetime.datetime.now().strftime('%Y-%m-%d %H:%M:%S') last_time = request.COOKIES.get('last_visit_time', "") response = render(request, 'cookie/index.html', {'username': username, 'last_time': last_time}) response.set_cookie('last_visit_time', now) return response else: return redirect('/cookie/login/')
index.html
Title Hi, {{ username }}
上次登录时间: {{ last_time }}
login.html
index
效果:
2.9,实例:Cookie版登陆校验
views视图代码:
from django.shortcuts import render, HttpResponse, redirect import datetime # Create your views here. # 登录验证装饰器 def login_auth(func): def inner(request, *args, **kwargs): # 获取请求的全路径 url = request.get_full_path() # /shopping/?nana=ppp # 服务器先拿到cookie,如果拿到则直接跳转到shopping或者order视图页面 is_login = request.COOKIES.get('is_login') # xxx = request.get_signed_cookie('xxx' ,salt='123') # 拿到cookie 判断是不是登录状态 if is_login: # 是登录状态直接调用被装饰的函数,返回我们想要的页面 ret = func(request, *args, **kwargs) else: # 服务端如果拿不到正确的cookie就会重定向了login这个页面,但是带了一些参数 return redirect('/login/?next=%s'%url) # 当清楚浏览器缓存是直接登录http://127.0.0.1:8000/shopping/,会跳转到登录页面 # 并且在url后面拼上http://127.0.0.1:8000/login/?next=/shopping/ # 127.0.0.1:8000/login/?next=/shopping/?nana=ppp return ret return inner def login(request): if request.method == 'POST': # 装饰器中redirect('/login/?next=%s'%url)通过get就可以拿到next的值 # url = request.GET.get('name') username = request.POST.get('username') password = request.POST.get('password') if username == 'james' and password == '123': # 登录成功后跳转到该页面 # obj = redirect(url) obj = HttpResponse("登录成功") # 当我们不指定path时,被装饰的视图函数页面只登录后都可以直接被访问,而不需要重新登录 obj.set_cookie('is_login', True) # 浏览器只会把cookie会传给带有该路径的页面 # 也就说当我们登录成功后,访问shopping页面时可以直接登录 # obj.set_cookie('is_login', True, path='/shopping/') # 但是我们访问order页面时,仍然会跳到登录页面让我们重新登录 now = datetime.datetime.now().strftime('%Y-%m-%d %X') # 设置cookie,在浏览器的请求头中的cookie中我们可以看到我们设置的额cookie # 并将登录时间也写到cookie中 obj.set_cookie('last_time', now) # 在浏览器中打开可以看到一个key obj.set_cookie('username','james') obj.set_cookie('password','123') # 获取cookie,并对cookie进行加盐,加盐后的123 是进行加密的遗传字符吗 obj.set_signed_cookie('james','durant',salt='123') # 登录成功后对其进行重定向 return obj else: obj = HttpResponse("登录失败") return obj return render(request, 'login.html') # 退出登录 def logout(request): obj = HttpResponse("注销成功") # 登录成功获得服务端发送来的cookie,删除cookie, # 这样我么在浏览器中在输入 http://127.0.0.1:8000/shopping/ # J就会在此跳到登录页面让我门重新登录 return obj # 购物页面 def shopping(request): return render(request, 'shopping.html', locals()) # locals()用法:locals()可以直接将函数中所有变量全部传给模板 # 当然这可能会传递一些多余的参数,有点浪费内存的嫌疑 # 登录成功后再请求shopping页面,在请求头就可以看到cookie中带有键值对的形式: # Cookie: csrftoken = ybQexReWXIWaO0If0p0I7NubfBSvjUlSfR2EWWX8eKCwXbL8lDo4Kk3ar6Nbr5ZR; # last_time = "2018-09-13 18:13:17"; # name = james; # pwd = 123; # xxx = xxxxxx:1g0Ocf: 1kRB9Q0FVXlVzstrRbm4fJ61eF0 # 订单页面 @login_auth def order(request): return render(request, 'order.html', locals())
url路由配置
from django.contrib import admin from django.conf.urls import url # 需要先导入对应的app的views文件 from cookieapp import views urlpatterns = [ url(r'^admin/', admin.site.urls), url(r'^login/', views.login), url(r'^order/', views.order), url(r'^shopping/',views.shopping), url(r'^logout/', views.logout), ]
template模板代码
login.html
Title
shopping.html
Title 购物车页面
order.html
Title 订单页面
结果展示:(登陆页面:127.0.0.1:8000/login.login)
登陆成功:
cookie展示
三,session概述
session就是在服务器端的“Cookie”,将用户数据保存在服务器端,远比保存在用户端要安全,方便和快捷的多。Session依赖于Cookie,但是与Cookie不同的地方就是在于session将所有的数据都放在服务器端,用户浏览器的cookie中只会保存一个非明文的识别信息,比如哈希值。
Session是服务器端技术,利用这个技术,服务器在运行时可以为每一个用户的浏览器创建一个其独享的session对象,由于session为用户浏览器独享,所以用户在访问服务器的web资源时,可以把各自的数据放在各自的session中,当用户再去访问该服务器中的其他web资源时,其他web资源再从用户各自的session中取出数据为用户服务。
Django的Session机器会向请求的浏览器发送cookie字符串。同时也会保存在本地一份,用来验证浏览器登录是否为同一用户。他存在于服务器,Django默认会把session存入数据库中。
Session依赖于Cookie,如果浏览器不能保存cookie,那么session就失效了。因为他需要浏览器的cookie和session做对比。session就是用来在服务器端保存用户的会话状态。
3.1,Django中session语法
class backends.base.SessionBase # 这是所有会话对象的基类,包含标准的字典方法: __getitem__(key) Example: fav_color = request.session['fav_color'] __setitem__(key, value) Example: request.session['fav_color'] = 'blue' __delitem__(key) Example: del request.session['fav_color'] # 如果不存在会抛出异常 __contains__(key) Example: 'fav_color' in request.session get(key, default=None) Example: fav_color = request.session.get('fav_color', 'red') pop(key, default=__not_given) Example: fav_color = request.session.pop('fav_color', 'blue')
类似于字典数据类型的内置方法
keys() items() setdefault() clear() # 它还有下面的方法: flush() # 删除当前的会话数据和会话cookie。经常用在用户退出后,删除会话。 set_test_cookie() # 设置一个测试cookie,用于探测用户浏览器是否支持cookies。由于cookie的 工作机制,你只有在下次用户请求的时候才可以测试。 test_cookie_worked() # 返回True或者False,取决于用户的浏览器是否接受测试cookie。你必须在之 前先调用set_test_cookie()方法。 delete_test_cookie() # 删除测试cookie。 set_expiry(value) # 设置cookie的有效期。可以传递不同类型的参数值: • 如果值是一个整数,session将在对应的秒数后失效。例如request.session. set_expiry(300) 将在300秒后失效. • 如果值是一个datetime或者timedelta对象, 会话将在指定的日期失效 • 如果为0,在用户关闭浏览器后失效 • 如果为None,则将使用全局会话失效策略 失效时间从上一次会话被修改的时刻开始计时。 get_expiry_age() # 返回多少秒后失效的秒数。对于没有自定义失效时间的会话,这等同于SESSION_COOKIE_AGE. # 这个方法接受2个可选的关键字参数 • modification:会话的最后修改时间(datetime对象)。默认是当前时间。 •expiry: 会话失效信息,可以是datetime对象,也可以是int或None get_expiry_date() # 和上面的方法类似,只是返回的是日期 get_expire_at_browser_close() # 返回True或False,根据用户会话是否是浏览器关闭后就结束。 clear_expired() # 删除已经失效的会话数据。 cycle_key() # 创建一个新的会话秘钥用于保持当前的会话数据。django.contrib.auth.login() 会调用这个方法
- 获取session:request.session[key]
- 设置session:request.session[key] = value
- 删除session:del request[key]
1、设置Sessions值 request.session['session_name'] ="admin" 2、获取Sessions值 session_name = request.session["session_name"] 3、删除Sessions值 del request.session["session_name"] 4、flush() 删除当前的会话数据并删除会话的Cookie。 这用于确保前面的会话数据不可以再次被用户的浏览器访问 5、get(key, default=None) fav_color = request.session.get('fav_color', 'red') 6、pop(key) fav_color = request.session.pop('fav_color') 7、keys() 8、items() 9、setdefault() 10 用户session的随机字符串
用户session的随机字符串举例:
request.session.session_key # 将所有Session失效日期小于当前日期的数据删除 request.session.clear_expired() # 检查 用户session的随机字符串 在数据库中是否 request.session.exists("session_key") # 删除当前用户的所有Session数据 request.session.delete("session_key") request.session.set_expiry(value) *如果value是个整数,session会在些秒数后失效。 *如果value是个datatime或timedelta,session就会在这个时间后失效。 *如果value是0, 用户关闭浏览器session就会失效。 *如果value是None, session会依赖全局session失效策略。
3.2,session配置
Django默认支持Session,并且默认是将Session数据存储在数据库中,即:django_session 表中。 a. 配置 settings.py SESSION_ENGINE = 'django.contrib.sessions.backends.db' # 引擎(默认) 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,默认修改之后才保存(默认)
3.3,session的作用
session下次通过cookie中的sessionID(键)获取用户信息值(值)
1,会话保持,记住用户的登录状态(WEB网站,分布式架构)
2,避免了敏感信息保存在客户端,防止客户端修改cookie信息(和cookie的区别)
session的过期时间
session的过期时间:django默认设置是2周 ,如果session过期,浏览器再携带之前的cookie就不能免登陆了。因为cookie已经失效了。
前端:如果超出了session的过期时间,那么浏览器会自动将对应的cookie删除掉
后端:django没有对过期的session做任何处理
如何删除后台保留的一些过期的session信息
python manage.py clearsessions
当然,如果用户在过期时间内主动退出登录,那么django会将该用户对应的session数据给删除掉 (request.session.flush())
但是如果用户在登录完以后没有主动退出,并且超出了过期时间,用户需要重新登录,但django中的过期session是不清除的,需要定期清理过期的session数据。
3.4,session ID的作用
当客户端第一次请求session时,服务器端会为客户端创造一个session对象,并且生成一个session ID (通过加密算法)。然后保存在cookie中,当用户再次登录时,客户端通过cookie,将session ID传到服务器,去和服务器中的session ID进行比对,寻找这个session,然后根据查找结果执行对应的操作。
因为session ID 是保存在cookie中,而cookie是存在于客户端,所以session ID 并不安全。
为了避免session ID 被盗,我们可以这样做:
- 1,敏感操作需要用户输入密码来进行二次认证
- 2,网站HTTPS化,提高消息传递过程中的安全系数
- 3,用户使用一个密钥对参数进行hash,这样即使cookie被盗取,也会因为没有密钥而无法获取sessionID。
3.5,session的保存方式
Session是大多数网站都需要具备的功能,Django为我们提供了一个通用的session框架,并且可以使用多种session数据的保存方式:
- 保存在数据库内
- 保存到缓存
- 保存到文件内
- 保存到cookie内
通常情况下,没有特别需求的话,请使用保存在数据库内的方式,尽量不要保存在cookie内。
django的session框架支持匿名会话,封装了cookies的发送和接收过程。cookie包含一个会话ID而不是数据本身(除非你使用的是基于后端的cookie)。
django的会话框架完全的,唯一的基于cookie。它不像PHP一样,把会话的ID放在URL中,那样不仅使得URL变得丑陋,而且使得你的网站易于受到通过“Referer”头部进行窃取会话ID的攻击。
3.6,session流程解析
session的应用要依赖于cookie(session就是cookie的变种)
1,每次用户第一次访问服务端,把用户的唯一字符串session_id加到cookie里面发送给客户端;
当用户登录之后,生成一个字典{key : value},将字典存入session,key是自动生成的一段字符串表示,返回cookie,value是一个自定义格式的字典。
2,服务器端保存随机字符串(sessionID :{用户信息}) 服务端
当我们在django中用到session时,cookie由服务端随机生成,写到浏览器的cookie中。每个浏览器都有自己的cookie值,是session寻找用户信息的唯一标识。每个浏览器请求到后台接受的request.session 等价于在1中的session字典key (cookie)对应的value。
3,session的好处就是客户端只有cookie的值,但是始终没有用户信息。
用法:request.session.get('k1') request.session['k1'] = 'v1'
session依赖于cookie,cookie保存在浏览器。session保存在服务器 端。
3.7,示例
注意:这里需要注意在session中,我们可以设置多个key:value的值,方便我们做很多事情,比如判断哪个用户:
如果用户登录后,那么他肯定有一个cookie,而当他访问购物车的时候,怎么判断是哪个用户呢?我们可以在session设置,当用户登录的时候,我们把用户名增加到session中,那么用户携带cookie访问的时候,我们就能判断是哪个用户来访问的。
比如下面的对应关系:
user1
cookie : 001
server session(举例格式)
{session:001 {'IS_LOGIN':'True',username:'james'}}
3.7.1,一段简单的Django中实现session的代码,判断用户是否已经成功登陆
from django.shortcuts import render,redirect,HttpResponse # Create your views here. def login(request): if request.method == 'POST': username = request.POST.get('username') password = request.POST.get('password') if username == 'james' and password == '123': # 设置session request.session['IS_Login'] = True return redirect('/app01/home') return render(request, 'login.html') def home(request): # 获取session is_login = request.session.get("IS_LOGIN", False) if is_login: return HttpResponse('order') else: return redirect('/app01/login/')
3.7.2 在视图中使用session
def session_test(request): # 设置session值,会将其存到db.sqlite数据库中的django_session表中 # 每发送一次请求就会在django_session表中添加一条记录 request.session['username'] = 'james' request.session['is_login'] = True ''' 1,生成一个随机字符串 2,把随机字符串以cookie的形式写回给浏览器 3,会在数据库中存{'随机字符串': {'username' : 'james' , 'is_login': True} ''' return HttpResponse("ok") def get_session(request): # 在浏览器中输入路由地址就会访问访问该视图函数 # 获取session值,但是一定要先获取session值,否则获取session时会报错 # name=request.session['username'] # is_login=request.session['is_login'] # print(name) # print(is_login) # session取值的两种方式,字典key或者get进行取值 # request.session['k1'] # request.session.get('k1', None) # 设置session的值 # request.session['k1'] = 123 # name=request.session.setdefault('username', 123) # 如果session中的username有值就不会设置,无值则进行设置 # print(name) #-----123 # del request.session['k1'] # 通过key将session的值进行删除 # print(request.session.session_key) # 取出表django_session表中session_key字段对应的值 # 将所有Session失效日期小于当前日期的数据删除 # request.session.clear_expired() # 判断session表中session_key字段的值是否存在,返回值为布尔值 # print(request.session.exists("b16mh23xajc2u69vazvivf8ruo4ilumi")) # 会删除数据库的session表中的session的记录,但是浏览器中的session id还在 # request.session.delete() # 删除当前的会话数据并删除会话的Cookie。 # 通常用它,他会把数据库以及浏览器会话中的session id都删除 # (调用视图函数test可以看到效果的确是已经被删除) request.session.flush() return HttpResponse("OK")
flush()方法是比较安全的一种做法,而且一次性将session中的所有内容全部清空,确保不留后患,但是也有不好的地方,那就是如果你在session中夹带了一点“私货”,会被一并删除,这一点一定要注意。
比如登出视图函数:
def logout(request): if not request.session.get('is_login', None): # 如果本来就未登录,也就没有登出一说 return redirect("/index/") request.session.flush() # 或者使用下面的方法 # del request.session['is_login'] # del request.session['user_id'] # del request.session['user_name'] return redirect("/index/")
3.7.3 在视图函数中使用装饰器装饰CBV
from django.utils.decorators import method_decorator # 在视图函数中装饰类 需要先导入method_decorator模块 from django.views import View # 在类上加装饰器需要指定name的值,也就是要指定装饰类内的那个函数 # 当我们登录成功后就会直接跳转到我们要登录的页面 # @method_decorator(login_auth, name='post') # @method_decorator(login_auth, name='get') class MyOrder(View): # 将装饰器传入进去 # @method_decorator(login_auth) # 我们可以重写dispatch函数来实现 def dispatch(self, request, *args, **kwargs): # 我们可以重写dispatch函数来实现类似于装饰器的效果 # dispatch 内部根据反射来实现函数执行 # 集成父类view的属性 ret = super().dispatch(request, *args, **kwargs) return ret # 在cbv上加装饰器,需要用method_decorator修饰一下 @method_decorator(login_auth) def get(self, request): return HttpResponse('get') def post(self,request): return HttpResponse('post') ''' 1 导入from django.utils.decorators import method_decorator 2 加载get,post,dispatch方法上:@method_decorator(login_auth) 3 加在类上:@method_decorator(login_auth,name='get') '''
四,cookie + session
1,cookie引入session
cookie看似解决了HTTP(短连接,无状态)的会话保持问题,但把全部用户数据保存在客户端,存在安全隐患。
于是cookie+session出现了,我们,我们可以把关于用户的数据保存在服务端,在客户端cookie里加一个sessionID(随机字符串)。
基于以上原因:cookie+session组成就成立了,并且结束了单单使用cookie做会话保持的方式。
2,Session和Cookie的好处
使用Session和Cookie的好处:Cookie可以理解为一个身份证ID,你只要拿着他去和Server端进行通信,如果你没有这个ID,那么server端也不知道你是谁。
举个例子:我在写博客的时候,在做Cookie和Session的实验,把Cookie删掉了,当我保存的时候直接给我提出来了,为什么呢?就是因为server端不知道我是谁了,因为我已经没有秘钥了。
所以,只要Session和Cookie任意一方失效,就可以理解为:Cookie失效相当于身份证ID过期,需要重新认证才可以继续使用。Session失效就相当于银行里的数据标识此ID无效,也需要重新申请。
3,cookie+session 的工作流程
1,当用户来访问服务端的时候,服务端生成一个随机字符串;
2,当用户登录成功后,把{sessionID : 随机字符串} 组织成键值对 加到cookie里发送给用户;
3,服务器以发送给客户端cookie中的随机字符串做键,用户信息做值,保存用户信息;
代码梳理
用户登录的两种方式:
1,使用login() 和logout()这两个内置函数实现登录和退出;缺点就是用户的登录时在操作同一个request.user。导致同一台电脑上不能同时登录两个用户;
2,如果同一台电脑的同一个网站,登录多个账号,为了防止串号,不能再使用login() 和logout()函数了,可以通过session和cookie来实现这个需求;
1,login.html
Title
2,home.html
Title {{ username }}
3,view.py
from django.shortcuts import render,redirect,HttpResponse # Create your views here. def login(request): if request.method == 'POST': username = request.POST.get('username') password = request.POST.get('password') if username == 'james' and password == '123': # 设置session request.session['IS_Login'] = True request.session['USERNAME'] = 'james' return redirect('/Manytable/home/') elif username == 'durant' and password == '123': request.session['IS_LOGIN'] = True request.session['USERNAME'] = 'durant' return redirect('/Manytable/home/') return render(request, 'Manytable/login.html') def home(request): # 获取session is_login = request.session.get("IS_LOGIN", False) if is_login: username = request.session.get("USERNAME", False) return render(request, 'Manytable/home.html', {'username':username}) # return HttpResponse('order') else: return redirect('/Manytable/login/')
4,mysite/urls.py
from django.contrib import admin from django.urls import path from django.conf.urls import url # 需要先导入对应的app的views文件 from Manytable import views # admin 后台的路由,先注释掉 urlpatterns = [ url(r'^admin/', admin.site.urls), # 你的路由,重点是引号中的正则表达式和后面的业务逻辑函数 url(r'^login/',views.login), url(r'^index/',views.home), ]
5,格式如下:
作业练习
1,登录案例
需要的页面: #login.html:登录页面,提供登录表单; #index1.html:主页,显示当前用户名称,如果没有登录,显示您还没登录; #index2.html:主页,显示当前用户名称,如果没有登录,显示您还没登录;
思考:如果第二个人在同一个浏览器上登录,Django-session表会怎么样呢?
2,验证码案例
验证码可以去识别发出请求的是人还是程序!当然,如果聪明的程序可以去分析验证码图片!但是分析图片也不是一件容易的事情,因为一般验证码图片都会带有干扰线,人都看不清,那么程序一定分析不出来。
参考文献:https://www.cnblogs.com/chenchao1990/p/5283725.html
https://blog.csdn.net/qq_32446743/article/details/79482536
https://www.jianshu.com/p/59cb3ecd81a4
https://www.cnblogs.com/sui776265233/p/9643055.html