Session是服务器端技术,利用这个技术,服务器在运行时可以为每一个用户的浏览器创建一个其独享的session对象,由于 session为用户浏览器独享,所以用户在访问服务器的web资源时 ,可以把各自的数据放在各自的session中,当用户再去访问该服务器中的其它web资源时,其它web资源再从用户各自的session中 取出数据为用户服务。
第一步:生成随机字符串; 第二步:服务器给浏览器的响应体里设置一个set_cookie,键为sessionid,值为对应的随机字符串。 第三步:在django-session表中生成一条记录,session-key为随机字符串,session-data是设置的session值["username"]="yuan"。 第四步:前面三步完成后,浏览器发第二次请求到某一视图函数中,这个函数如果要取session:request.session["username"]即可。这一步也分三步:(1)读cookie里的sessionid对应的随机字符串;(2)在django-session表中过滤session-key等于随机字符串的记录。(3)在过滤出的记录对象中取出session-data对应的字典,再.get("username")可以把对应的“yuan”取出。
# 1、设置Sessions值 request.session['session_name'] ="admin" # 2、获取Sessions值 session_name = request.session["session_name"] # 3、删除Sessions值 del request.session["session_name"] # 删除一组键值对 # 4、flush() request.session.flush() # 删除一条记录 删除当前的会话数据并删除会话的Cookie。 这用于确保前面的会话数据不可以再次被用户的浏览器访问 # 5、检测是否操作session值 if "session_name" is request.session:
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、flush() 删除当前的会话数据并删除会话的Cookie。 这用于确保前面的会话数据不可以再次被用户的浏览器访问 例如,django.contrib.auth.logout() 函数中就会调用它。 11、用户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失效策略。
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,默认修改之后才保存(默认)
SESSION_SAVE_EVERY_REQUEST=True:意味着每次登录都刷新SESSION_COOKIE_AGE时间计算,推迟cookie的失效时间。
urls.py中添加两条新路径信息:
# session path('login_session/', views.login_session), path('index_session/', views.index_session),
views.py中添加两个session的视图函数:
def login_session(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: # 登录成功 # 写session request.session["is_login"] = True request.session["username"] = user.user """ 1、生成一个随机字符串 ,如:234sd23asdjd32 2、response.set_cookie("sessionid", 234sd23asdjd32) 3、在django-session表中创建一条记录: session-key session-data 234sd23asdjd32 {"is_login":True, "username":"yuan"} """ return HttpResponse("登录成功!") return render(request, "login.html") def index_session(request): # 读session print("is_login", request.session.get("is_login")) # is_login True或者输出is_login None """ 1、request.COOKIE.get("session") # 得到随机字符串 2、在django-session表中过滤记录: django-session.objects.filter(session-key=随机字符串).first 拿到记录对象 3、obj.session-data.get("is_login") """ is_login = request.session.get("is_login") if not is_login: # 未通过登录校验 return redirect("/login_session/") # 已通过登录校验,显示登录人名称 username = request.session.get("username") return render(request, "index.html", {"username": username})
注意:写session和读session的方法和执行步骤。
在login_session视图函数中添加:
# 添加在登录成功,写session那一块中 import datetime now = datetime.datetime.now().strftime("%Y-%m-%d %H:%M:%S") request.session["last_visit_time"] = now
在index_session视图函数中添加:
# 在已通过登录校验部分得到last_visit_time的值 last_visit_time = request.session.get("last_visit_time") # 将last_visit_time传入模板 return render(request, "index.html", {"username": username, "last_visit_time": last_visit_time})
用户登录时,会进行写session操作。 在实际情况下,如果用chrome浏览器反复登录yuan用户。可以发现数据库中的django_session表条数并没有增加。 反复更新时,session_key保持不变,仅对session_data进行了覆盖更新。由此可知: 服务器会首先判断能否request.COOKIE.get("sessionid")是否有结果,如果是第一次登录没有结果则执行添加操作,反之则进行更新操作。即服务器和浏览器永远只用一条记录来维持。用户切换,则将之前用户的信息完全覆盖。 但使用firefox等浏览器登录时,会进行添加操作。因此服务器是用一条记录与浏览器对应而不是针对用户。
index.html:
<h3>HI,{{ username }}</h3> {#<p>上次登录时间: {{ last_time }}</p>#} <p>上次登录时间: {{ last_visit_time }}</p> <a href="/logout/">注销</a>
urls.py:
path('logout', views.logout)
views.py:
def logout(request): # del request.session["is_login"] # 删除了is_login这一个键值,但用来做注销并不适用 request.session.flush() """ 1、random_str = request.COOKIE.get("sessionid") 2、django-session.object.filter(session-key=random_str).delete() 3、清cookie:response.delete_cookie("sessionid", random_str) """ return redirect("/login")
注意两种session删除方法的区别!!
'''需要借助于一个专门的装饰器模块''' from django.utils.decorators import method_decorator
class MyLoginView(views.View): @method_decorator(login_auth) def get(self, request): return HttpResponse("from CBV get view")
name为具体要加的函数
@method_decorator(login_auth, name='get') class MyLoginView(views.View): def get(self, request): return HttpResponse("from CBV get view")
class MyLoginView(views.View): @method_decorator(login_auth) def dispatch(self, request, *args, **kwargs): super().dispatch(request,*args,**kwargs)
官方的说法:中间件是一个用来处理Django的请求和响应的框架级别的钩子。它是一个轻量、低级别的插件系统,用于在全局范围内改变Django的输入和输出。每个中间件组件都负责做一些特定的功能。 但是由于其影响的是全局,所以需要谨慎使用,使用不当会影响性能。 说的直白一点中间件是帮助我们在视图函数执行之前和执行之后都可以做一些额外的操作,它本质上就是一个自定义类,类中定义了几个方法,Django框架会在请求的特定的时间去执行这些方法。
我们一直都在使用中间件,只是没有注意到而已,打开Django项目的Settings.py文件,看到下方的MIDDLEWARE配置项。
MIDDLEWARE = [ 'django.middleware.security.SecurityMiddleware', 'django.contrib.sessions.middleware.SessionMiddleware', 'django.middleware.common.CommonMiddleware', 'django.middleware.csrf.CsrfViewMiddleware', 'django.contrib.auth.middleware.AuthenticationMiddleware', 'django.contrib.messages.middleware.MessageMiddleware', 'django.middleware.clickjacking.XFrameOptionsMiddleware', ]
# MIDDLEWARE配置项是一个列表,列表中是一个个字符串,这些字符串其实是一个个类,也就是一个个中间件。
""" 1.创建一个任意名称的文件夹 2.在该文件夹内创建一个任意名称的py文件 3.在该py文件内编写中间件类 4.配置文件中注册 """ # 必须要掌握的方法 1.'process_request(self,request)' 请求来的时候会从上往下依次执行配置文件中注册了的中间件里面的process_request方法 如果没有则直接跳过 如果该方法自己返回了HttpResponse对象,那么请求不再继续往后直接返回相应的数据 2.'process_response(self, request, response)' 响应走的时候会从下往上依次执行配置文件中注册了的中间件里面的process_response方法 如果没有则直接跳过 如果该方法自己返回了HttpResponse对象,那么响应会替换成该HttpResponse对象数据 而不再是视图函数想要返回给客户端的数据 """如果process_request返回了HttpResponse对象 那么会从当前位置从下往上执行每一个process_response""" # 需要了解的方法 1.'process_view(self, request, view_func, view_args, view_kwargs)' 路由匹配成功之后执行视图之前从上往下执行配置文件中注册了的中间件里面的process_view方法 2.'process_template_response(self,request,response)' 视图函数执行完毕之后返回的对象中含有render属性对应一个render方法 则会从下往上执行配置文件中注册了的中间件里面的process_template_response方法 3.'process_exception(self, request, exception)' 视图函数执行过程中报错并在返回响应的时候会从下往上执行配置文件中注册了的中间件里面的process_exception
from django.utils.deprecation import MiddlewareMixin class MD1(MiddlewareMixin): def process_request(self, request): print("MD1里面的 process_request") def process_response(self, request, response): print("MD1里面的 process_response") return response
CSRF跨站点请求伪造(Cross—Site Request Forgery):大概可以理解为攻击者盗用了你的身 份,以你的名义在恶意网站发送恶意请求,对服务器来说这个请求是完全合法的,但是却完成了攻击 者所期望的一个操作,比如以你的名义发送邮件、发消息,盗取你的账号,甚至于购买商品、转账等。
# form表单 <form action="" method="post"> {% csrf_token %} <p>当前账户:<input type="text" name="current_user"></p> <p>目标账户:<input type="text" name="target_user"></p> <p>转账金额:<input type="text" name="money"></p> <input type="submit"> </form> # ajax请求 1.方式1:页面任意位置先写{% csrf_token %} 之后获取数据 'csrfmiddlewaretoken':$('input[name="csrfmiddlewaretoken"]').val() 2.方式2:模板语法直接获取 'csrfmiddlewaretoken':{{ csrf_token }} ''' <script> $('#d1').click(function () { $.ajax({ url:'', type:'post', data:{ 'current_user':$('input[name="current_user"]').val(), 'target_user':$('input[name="target_user"]').val(), 'money':$('input[name="money"]').val(), {#'csrfmiddlewaretoken':$('input[name="csrfmiddlewaretoken"]').val()#} {#'csrfmiddlewaretoken':{{ csrf_token }}#} }, success:function (args) { alert(args) } }) }) </script> '''
也只能适用于ajax提交 form表单还是需要额外指定
JS: function getCookie(name) { var cookieValue = null; if (document.cookie && document.cookie !== '') { var cookies = document.cookie.split(';'); for (var i = 0; i < cookies.length; i++) { var cookie = jQuery.trim(cookies[i]); // Does this cookie string begin with the name we want? if (cookie.substring(0, name.length + 1) === (name + '=')) { cookieValue = decodeURIComponent(cookie.substring(name.length + 1)); break; } } } return cookieValue; } var csrftoken = getCookie('csrftoken'); function csrfSafeMethod(method) { // these HTTP methods do not require CSRF protection return (/^(GET|HEAD|OPTIONS|TRACE)$/.test(method)); } $.ajaxSetup({ beforeSend: function (xhr, settings) { if (!csrfSafeMethod(settings.type) && !this.crossDomain) { xhr.setRequestHeader("X-CSRFToken", csrftoken); } } });