4.Cookie和Session
cookie与session的简介
网站的发展史
刚开始的网站没有保存用户的功能的需求,所有用户访问返回的结果都是一样的(eg:新闻、博客、文章...) 后来出现了一些需要保存用户信息的网站(eg:淘宝、京东、支付宝...) # 以登录功能为例:如果不保存用户登录状态,也就意味着用户每次访问网站都需要输入用户名和密码 #当用户第一次登陆成功之后,服务端会将用户的用户名和密码返回给浏览器并让浏览器保存在本地;当浏览器再次访问时,浏览器会将保存在本地的用户名和密码发送给服务端,服务端获取之后自动验证 早期这种方式存在非常大的安全隐患 # 当用户第一次登陆成功之后,服务端会产生一个随机字符串(在服务端以kv键值对的形式保存数据),交由浏览器保存
随机字符串1:用户1相关信息
随机字符串2:用户2相关信息
当浏览器再次访问时会带上该随机字符串,服务端会去数据库中对比是否有对应的随机字符串从而获取到对应的用户信息 如果截取到该字符串,则可以冒充当前用户,存在安全隐患
cookie
什么是cookie
服务端保存在客户端浏览器上的信息都可以称之为cookie,它的表现形式一般都是k:v键值对(可以有多个)
cookie的原理
cookie的工作原理:由服务器产生内容,浏览器收到请求后保存到本地;当浏览器再次访问时,浏览器会自动带上cookie,这样服务器就能通过cookie的内容进行判断。
查看cookie
session
数据保存在服务端的并且表现形式一般也是k:v键值对(可以有多个)
token(了解)
''' session虽然数据是保存在服务端,但是禁不住数据量大 服务端不再保存数据 登陆之后将一段用户信息进行加密处理(加密算法只有该公司开发人员知道) 将加密之后的结果拼接在信息后面,整体返回给浏览器保存 浏览器下次访问时带上该信息,服务端自动切去前面一段信息再次使用自己的加密算法,更浏览器的尾部密文进行比对 '''
Django中操作Cookie
cookie的操作
# 获取cookie request.COOKIES.get(key) request.get_signed_cookie(key, default=RAISE_ERROR, salt='', max_age=None) # 设置cookie
# 如果你想要操作cookie,你就不得不利用obj对象 obj1 = HttpResponse(...) obj2 = render(request, ...)
obj1.set_cookie(key,value,...) obj2.set_signed_cookie(key,value,salt='加密盐', max_age=None, ...) # 删除cookie def logout(request): rep = redirect("/login/") rep.delete_cookie("user") # 删除用户浏览器上之前设置的usercookie值 return rep
常用参数
- default:默认值
- salt:加密盐
- key,键
- value='',值
- max_age=None,超时时间
- expires=None,超时时间
- path='/', Cookie生效的路径,/ 表示根路径,特殊的:根路径的cookie可以被任何url的页面访问
- domain=None, Cookie生效的域名
- secure=False, https传输
- httponly=False 只能http协议传输,无法被JavaScript获取(不是绝对,底层抓包可以获取到也可以被覆盖)
cookie版登陆功能
from django.contrib import admin from django.urls import path,re_path from app01 import views urlpatterns = [ path('admin/', admin.site.urls), # 登陆功能 re_path(r'^login/',views.login), re_path(r'^home/',views.home), re_path(r'^index/',views.index), # 注销功能 re_path(r'^logout/',views.logout), ]
<body> <form action="" method="post"> <p>username <input type="text" name="username"></p> <p>password <input type="text" name="password"></p> <input type="submit"> </form> </body>
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 def login(request): if request.method == 'POST': username = request.POST.get('username') password = request.POST.get('password') if username == 'yuanxiaojiang' and password == '2589': # 获取上一次访问的url target_url = request.GET.get('next') # 这个结果可能是None if target_url: obj = redirect(target_url) else: # 保存用户的登陆状态 obj = redirect('/home/') # 让浏览器记录cookie数据 obj.set_cookie('username','jiangxx') # 浏览器不紧帮你存,而且后面每次访问你的时候还会带着它过来 return obj return render(request,'login.html') @login_auth def home(request): # # 获取cookie信息,判断有没有对应的cookie信息 # if request.COOKIES.get('username') == 'jiangxx': # return HttpResponse('home页面,只有登陆的页面才能进来') # #没有登陆跳转到登陆页面 # return redirect('/login/') return HttpResponse('home页面,只有登陆的页面才能进来') @login_auth def index(request): # # 获取cookie信息,判断有没有对应的cookie信息 # if request.COOKIES.get('username') == 'jiangxx': # return HttpResponse('index页面,只有登陆的页面才能进来') # # 没有登陆跳转到登陆页面 # return redirect('/login/') return HttpResponse('index页面,只有登陆的页面才能进来') @login_auth def logout(request): obj = redirect('/login/') obj.delete_cookie('username') return obj
Django中操作session
django中session相关方法
# 设置、获取、删除session中数据 request.session['key'] = value request.session.get('key') request.session.delete() # 只删服务端中的session数据,浏览器的不删 request.session.flush() # 服务端和浏览器都清空(推荐使用) # 所有 键、值、键值对 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。 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失效策略。
# session数据保存在服务端,会给客户端返回一个随机字符串 sessionid:随机字符串 # 默认情况下操作session时需要django默认的一张django_session表 数据库迁移命令 django会自己创建多张表,django_session就是其中一张 # django默认session的过期时间为14天 可以修改过期时间 # django_session表中的数据条数取决于浏览器 同一个计算机上(IP地址)同一个浏览器只会有一条数据生效 (当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版登陆功能
Django中session配置
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实现登录视图
class LoginView(View): def get(self, request): """ 处理GET请求 """ return render(request, 'login.html') def post(self, request): """ 处理POST请求 """ user = request.POST.get('user') pwd = request.POST.get('pwd') if user == 'alex' and pwd == "alex1234": next_url = request.GET.get("next") # 生成随机字符串 # 写浏览器cookie -> session_id: 随机字符串 # 写到服务端session: # { # "随机字符串": {'user':'alex'} # } request.session['user'] = user if next_url: return redirect(next_url) else: return redirect('/index/') return render(request, 'login.html')
要在CBV视图中使用我们上面的check_login装饰器,有以下三种方式:
from django.utils.decorators import method_decorator
加在CBV视图的get或post方法上
from django.utils.decorators import method_decorator class HomeView(View): def dispatch(self, request, *args, **kwargs): return super(HomeView, self).dispatch(request, *args, **kwargs) def get(self, request): return render(request, "home.html") @method_decorator(check_login) def post(self, request): print("Home View POST method...") return redirect("/index/")
加在dispatch方法上
from django.utils.decorators import method_decorator class HomeView(View): @method_decorator(check_login) def dispatch(self, request, *args, **kwargs): return super(HomeView, self).dispatch(request, *args, **kwargs) def get(self, request): return render(request, "home.html") def post(self, request): print("Home View POST method...") return redirect("/index/")
因为CBV中首先执行的就是dispatch方法,所以这么写相当于给get和post方法都加上了登录校验。
直接加在视图类上,但method_decorator必须传 name 关键字参数
如果get方法和post方法都需要登录校验的话就写两个装饰器。
from django.utils.decorators import method_decorator @method_decorator(check_login, name="get") @method_decorator(check_login, name="post") class HomeView(View): def dispatch(self, request, *args, **kwargs): return super(HomeView, self).dispatch(request, *args, **kwargs) def get(self, request): return render(request, "home.html") def post(self, request): print("Home View POST method...") return redirect("/index/")
补充
CSRF Token相关装饰器在CBV只能加到dispatch方法上,或者加在视图类上然后name参数指定为dispatch方法。
备注:
- csrf_protect,为当前函数强制设置防跨站请求伪造功能,即便settings中没有设置全局中间件。
- csrf_exempt,取消当前函数防跨站请求伪造功能,即便settings中设置了全局中间件。
from django.views.decorators.csrf import csrf_exempt, csrf_protect from django.utils.decorators import method_decorator class HomeView(View): @method_decorator(csrf_exempt) def dispatch(self, request, *args, **kwargs): return super(HomeView, self).dispatch(request, *args, **kwargs) def get(self, request): return render(request, "home.html") def post(self, request): print("Home View POST method...") return redirect("/index/")
或者
from django.views.decorators.csrf import csrf_exempt, csrf_protect from django.utils.decorators import method_decorator @method_decorator(csrf_exempt, name='dispatch') class HomeView(View): def dispatch(self, request, *args, **kwargs): return super(HomeView, self).dispatch(request, *args, **kwargs) def get(self, request): return render(request, "home.html") def post(self, request): print("Home View POST method...") return redirect("/index/")