cookie和session
如何使用会话 | Django 文档 | Django (djangoproject.com)
一、cookie和session简介
1、背景
HTTP协议的特性之一:无状态 痛点:访问网站,每次需要重新从浏览器登录。为了保存用户状态产生了cookie技术,只要cooike值还在浏览器中,浏览器自动和服务器端验证,不需要用户再一次手动登录。 但是数据保存在浏览器上面,很明显的问题是:数据不够安全。做了优化:把原本存在浏览器上的数据存到后端,就称之为是session。session就解决了cookie数据不安全的问题
2、cookie原理
是一种存储在用户计算机上的小数据片段,用于跟踪用户在网站上的活动并存储一些用户偏好。它是通过网页服务器发送到用户的浏览器,然后由浏览器存储并在每次请求特定网站时将其发送回服务器的。
1 创建和发送 Cookie:当用户访问一个网站时,网站的服务器可以向用户的浏览器发送一个包含一些数据的 Cookie。这个数据可以包括用户的身份验证信息、访问时间、用户偏好等等。 2 存储 Cookie:一旦用户的浏览器接收到服务器发来的 Cookie,它会将 Cookie 存储在用户计算机的一个特定位置,通常是浏览器的 Cookie 存储区域。每个 Cookie 都有一个名称、一个值以及一些可选属性(如过期时间、域、路径等)。 3 发送 Cookie:在用户之后的每个请求中,浏览器都会自动将与当前网站相关的 Cookie 发送回服务器。这允许服务器识别用户、跟踪用户的会话状态,或根据用户的偏好提供定制化的体验。 4 服务器处理 Cookie:服务器收到 Cookie 后,可以根据其中的数据进行各种操作,比如识别用户、保持用户的登录状态、跟踪用户的浏览历史等等。 5 过期和删除:每个 Cookie 都可以设置一个过期时间,过了这个时间,浏览器将不再发送该 Cookie。此外,服务器也可以要求浏览器删除某个特定的 Cookie,或者用户自己在浏览器中删除 Cookie。
3、session原理
与 Cookie 不同,Session 数据存储在服务器上而不是用户的浏览器中。Session 通常用于跟踪用户的登录状态、存储用户数据、在不同页面之间共享数据等。
1 会话创建:当用户第一次访问网站时,服务器会为该用户创建一个新的会话。服务器为每个会话分配一个唯一的标识符,通常称为“Session ID”。 2 Session ID:服务器会将 Session ID 发送给用户的浏览器,通常通过 Cookie(称为 Session Cookie)的方式。这个 Cookie 包含了 Session ID,使得浏览器在后续的请求中能够告诉服务器该请求属于哪个会话。 3 数据存储:在会话中,服务器可以存储用户的数据。这些数据可以是用户的登录信息、购物车内容、用户偏好设置等等。服务器会根据 Session ID 来区分不同用户的数据。 4 数据访问:当用户在不同页面之间跳转或与网站进行交互时,浏览器会自动将包含 Session ID 的 Cookie 发送回服务器。服务器根据 Session ID 找到相应的会话,并访问其中的数据,以便在不同页面之间保持用户状态或共享数据。 5 会话过期:会话通常具有一定的过期时间。一旦用户一段时间内没有活动,或者会话达到设定的过期时间,服务器会自动清除会话数据,从而释放服务器资源。用户需要重新进行登录或者重新开始新的会话。

4、面试点
1. 保存在浏览器上的数据都称之为是cookie 2. session是保存在服务端的 3. session的数据相对更加安全,cookie不够安全 4. session是基于cookie工作的? 对还是不对? 对 5. django让浏览器保存cookie,用户有权可以设置浏览器不保存 6. session离开cookie一定就不能工作了,对还是不对? 不对
二、
在Django中,可以使用内置的
request和三板斧(render, redirect, HttpResponse)对象来操作cookie
1、设置cookie(将cookie存在浏览器)
from django.http import HttpResponse
def set_cookie(request):
response = HttpResponse("Cookie has been set!")
response.set_cookie('username', 'myuser', max_age=3600) # 设置cookie的名称、值和过期时间(秒)
return response
注意参数:
- key, 键
- value=’’, 值
- max_age=None, 超时时间 cookie需要延续的时间(以秒为单位)如果参数是\ None`` ,这个cookie会延续到浏览器关闭为止
- expires=None, 超时时间(IE requires expires, so set it if hasn’t been already.)
- path=’/‘, Cookie生效的路径,/ 表示根路径
- ecure=False, 浏览器将通过HTTPS来回传cookie
- httponly=False 只能http协议传输,无法被JavaScript获取
2、获取cookie
from django.http import HttpResponse
def get_cookie(request):
username = request.COOKIES.get('username', 'Guest') # 如果cookie不存在,使用默认值'Guest'
return HttpResponse("Hello, " + username)
3、删除cookie(用在退出登录)
from django.http import HttpResponse
import datetime
def delete_cookie(request):
response = HttpResponse("Cookie has been deleted!")
response.delete_cookie('username') # 删除名为'username'的cookie
return response
三、
1、需要知道的概念
session的数据是保存在后端,保存在后端的载体其实有很多种,比如:可以把数据保存在数据库、文件、Redis等
Django的默认保存位置在数据库中,在django_session表中,这张表是默认生成的
session的默认过期时间是14天
2、获取session
- 获取session发生了哪些事?
- 浏览器先把sessionid回传到Django的后端
- Django后端获取到sessionid,然后去数据表中根据session_key查询
- # 如果查到了,说明之前已经登陆过了
- # 如果查不到,就返回None
- 查询出来的数据默认是加密的,Django后端又把数据解密之后封装到request.session中
- #在取session值的时候,就从request.session中取
from django.http import HttpResponse
def get_session(request):
# 获取会话数据
username = request.session.get('username', 'Guest')
is_logged_in = request.session.get('is_logged_in', False)
return HttpResponse("Username: {}, Logged in: {}".format(username, is_logged_in))
注意:
request.session[key]: 通过键(key)获取会话数据的值。request.session.get(key, default=None): 获取会话数据的值,如果键不存在,返回默认值。
3、 设置session
设置成功一个session值有什么变化
- 会生成一个随机字符串
- 会把用户设置的信息保存在django_session表中,数据也做了加密处理
- 把数据封装到了request.session里去了
- Django后端把随机字符串保存到了浏览器中
- 随机字符串保存在浏览器中的key=sessionid
- 当设置多个session值的时候,session_key是不变的,变的是session_Data
- 当设置多个session值的时候,django_Session表中只存在一条记录(一台电脑的一个浏览器)
from django.http import HttpResponse
def set_session(request):
# 设置会话数据
request.session['username'] = 'myuser'
request.session['is_logged_in'] = True
return HttpResponse("Session data has been set!")
4、 删除session
from django.http import HttpResponse
def delete_session(request):
# 删除会话数据
if 'username' in request.session:
del request.session['username']
if 'is_logged_in' in request.session:
del request.session['is_logged_in']
return HttpResponse("Session data has been deleted!")
注:
del request.session[key]: 通过键(key)删除会话数据。request.session.pop(key, None): 删除会话数据,如果键不存在,不抛出异常。
5、session数据操作:
request.session.keys(): 获取会话中所有键的列表。request.session.values(): 获取会话中所有值的列表。request.session.items(): 获取会话中所有键值对的列表。request.session.clear(): 清空会话中的所有数据。request.session.session_key: 获取会话的唯一标识符(session ID)
退出登录,session版本 session.flush()
def logout(request):
request.session.flush()
return redirect('/login/')
request.session.flush() 是Django框架中用于清空会话数据的方法。当你调用这个方法时,会话中保存的所有数据都会被删除,包括用户登录状态、用户的临时数据等。这在某些情况下非常有用,例如:
-
注销用户:当用户登出或注销时,你可以使用
request.session.flush()来删除与用户相关的会话数据,以确保用户的隐私和安全。 -
限制会话时长:有时你可能希望会话在一段时间后过期,以确保用户在一段时间内没有活动时会话数据会被清除。
-
数据隔离:当用户切换到不同的角色或状态时,你可能希望清除之前的会话数据,以确保不同状态之间的数据不会相互干扰。
-
数据重置:如果用户执行某些特定操作后,你可能需要重置会话以确保操作后的会话状态是干净的。
四、
django项目的settings.py文件中可以配置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,默认修改之后才保存(默认)
session 设置过期时间
在 Django 中,可以使用 request.session.set_expiry 方法来动态设置会话的过期时间。作用在特定视图或代码块中更改会话的过期时间,而不是在全局设置中固定一个值。
from django.shortcuts import render
def my_view(request):
# 设置会话的过期时间为60秒
request.session.set_expiry(60)
# 在会话中存储数据
request.session['user_id'] = 123
# 从会话中获取数据
user_id = request.session.get('user_id')
return render(request, 'my_template.html', {'user_id': user_id})
五、基于cookie的登录功能,用户名和密码从数据表中读取!
1、html 简单的登录页面
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Title</title>
<link href="https://cdn.bootcdn.net/ajax/libs/twitter-bootstrap/3.3.6/css/bootstrap.min.css" rel="stylesheet">
<script src="https://cdn.staticfile.org/jquery/2.1.1/jquery.min.js"></script>
<script src="https://cdn.bootcdn.net/ajax/libs/twitter-bootstrap/3.3.6/js/bootstrap.min.js"></script>
</head>
<body>
<h1>这里是登录页面</h1>
<br>
<form action="" method="post" enctype="application/x-www-form-urlencoded">
username:<input type="text" name="username">
<br>
<br>
password:<input type="password" name="password">
<br>
<br>
提交:<input type="submit">
</form>
</body>
</html>
2、views 后端处理逻辑
验证登录页面、给主页加上认证装饰器。当cookie过期,访问主页自动跳转到登录页面。
# 登录页面
def login(request):
if request.method == 'POST':
# 从前端获取用户输入的帐号密码
username = request.POST.get('username')
password = request.POST.get('password')
# 读取数据库中的账号密码
user_obj = models.User.objects.filter(name=username, password=password).first()
print(user_obj)
if user_obj:
print('登录成功!')
# 保存用户的信息,使用cookie保存
obj = redirect('/home/')
obj.set_cookie('username', user_obj.name, max_age=10)
return obj
return render(request, 'login.html')
# 登录认证装饰器
def login_auth(func):
def inner(request, *args, **kwargs):
if request.COOKIES.get('username'):
return func(request, *args, **kwargs)
else:
return redirect('/login/')
return inner
# 主页加上登录认证装饰器
@login_auth
def home(request):
'''访问这个home页面,必须登录之后才能范围,否则不让访问?'''
# 判断用户是否登录了?
# 就是判断是否有cookie
# print(request.COOKIES.get('username'))
# if request.COOKIES.get('username'):
# return HttpResponse("登录之后才能看到我哦")
# else:
# return redirect('/login/')
return render(request, 'home.html')
注意⚠️:
1. obj.set_cookie('username', user_obj.name, max_age=10)
max_age=10是设置cookie 10秒过期
2. 查看cookie保存的值

六、登录显示用户名、退出登录
1、views
# 退出登录
def offline(request):
response = redirect('/login1/')
response.delete_cookie('username') # 删除名为 'username' 的 Cookie
return response
注意:
使用三板斧创建一个response对象,这里用redirect返回一个登录页面
通过这个对象点方法去删除cookie
2、html
home页面动态获取登录用户名、退出登录跳转到offline视图函数
<ul class="nav navbar-nav navbar-right">
<li><a href="#">{{ login1_name }}</a></li>
<li class="dropdown">
<a href="#" class="dropdown-toggle" data-toggle="dropdown" role="button" aria-haspopup="true"
aria-expanded="false">更多操作<span class="caret"></span></a>
<ul class="dropdown-menu">
<li><a href="/offline/">退出登录</a></li>
<li><a href="#">刷新</a></li>
<li><a href="#">待开发</a></li>
<li role="separator" class="divider"></li>
<li><a href="#">待开发</a></li>
</ul>
</li>
</ul>
3、登录界面
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Document</title>
<style>
body {
background: url('https://pic2.zhimg.com/3ae866e7992a94069c7e0c417aac807d_r.jpg') no-repeat;
background-size: 100% 130%;
}
#login_box {
width: 20%;
height: 400px;
background-color: #00000060;
margin: auto;
margin-top: 10%;
text-align: center;
border-radius: 10px;
padding: 50px 50px;
}
h2 {
color: #ffffff90;
margin-top: 5%;
}
#input-box {
margin-top: 5%;
}
span {
color: #fff;
}
input {
border: 0;
width: 60%;
font-size: 15px;
color: #fff;
background: transparent;
border-bottom: 2px solid #fff;
padding: 5px 10px;
outline: none;
margin-top: 10px;
}
button {
margin-top: 50px;
width: 60%;
height: 30px;
border-radius: 10px;
border: 0;
color: #fff;
text-align: center;
line-height: 30px;
font-size: 15px;
background-image: linear-gradient(to right, #30cfd0, #330867);
}
#sign_up {
margin-top: 45%;
margin-left: 60%;
}
a {
color: #b94648;
}
</style>
</head>
<body>
<div id="login_box">
<h2>图书管理</h2>
<form action="" method="post">
<div id="input_box">
<input type="text" placeholder="请输入用户名" name="username">
</div>
<div class="input_box">
<input type="password" placeholder="请输入密码" name="password">
</div>
<button>登录</button>
<br>
</form>
</div>
</body>
</html>

七、基于session的登录功能,用户名和密码从数据表中读取!
1、views
request.session['username'] = user_obj.name 保存session到django_session数据库
def login2(request):
if request.method == 'POST':
# 从前端获取用户输入的帐号密码
username = request.POST.get('username')
password = request.POST.get('password')
# 读取数据库中的账号密码
user_obj = models.User.objects.filter(name=username, password=password).first()
if user_obj:
print('登录成功!')
# 保存用户的信息,使用session方式保存
request.session['username'] = user_obj.name
return redirect('/book_list/')
return render(request, 'login.html')
2、登录认证装饰器session版
def login_auth1(func):
def inner(request, *args, **kwargs):
if 'username' in request.session and request.session['username']:
return func(request, *args, **kwargs)
else:
return redirect('/login2/')
return inner
3、给图书展示页面加上装饰器
@login_auth1
def book_list(request):
# 分页器代码
current_page = request.GET.get('page')
try:
current_page = int(current_page)
except Exception:
current_page = 1
book_list = models.Book.objects.all()
all_count = book_list.count()
page_pbj = Pagination(current_page, all_count, per_page_num=4)
book_list = book_list[page_pbj.start:page_pbj.end] # 前端循环取数据
page_html = page_pbj.page_html()
return render(request, 'book_list.html', locals())
4、同一个浏览器不同的用户,比如:zjz、ldj,django_session 数据表中只会存在一条记录


当使用不同的浏览器登录时, 数据库中才会多存一条记录


八、分页器
1、 封装分页相关数据(在Django中utils引用)
class Pagination(object):
def __init__(self, current_page, all_count, per_page_num=2, pager_count=11):
"""
封装分页相关数据
:param current_page: 当前页
:param all_count: 数据库中的数据总条数
:param per_page_num: 每页显示的数据条数
:param pager_count: 最多显示的页码个数
"""
try:
current_page = int(current_page)
except Exception as e:
current_page = 1
if current_page < 1:
current_page = 1
self.current_page = current_page
self.all_count = all_count
self.per_page_num = per_page_num
# 总页码
all_pager, tmp = divmod(all_count, per_page_num)
if tmp:
all_pager += 1
self.all_pager = all_pager
self.pager_count = pager_count
self.pager_count_half = int((pager_count - 1) / 2)
@property
def start(self):
return (self.current_page - 1) * self.per_page_num
@property
def end(self):
return self.current_page * self.per_page_num
def page_html(self):
# 如果总页码 < 11个:
if self.all_pager <= self.pager_count:
pager_start = 1
pager_end = self.all_pager + 1
# 总页码 > 11
else:
# 当前页如果<=页面上最多显示11/2个页码
if self.current_page <= self.pager_count_half:
pager_start = 1
pager_end = self.pager_count + 1
# 当前页大于5
else:
# 页码翻到最后
if (self.current_page + self.pager_count_half) > self.all_pager:
pager_end = self.all_pager + 1
pager_start = self.all_pager - self.pager_count + 1
else:
pager_start = self.current_page - self.pager_count_half
pager_end = self.current_page + self.pager_count_half + 1
page_html_list = []
# 添加前面的nav和ul标签
page_html_list.append('''
<nav aria-label='Page navigation>'
<ul class='pagination'>
''')
first_page = '<li><a href="?page=%s">首页</a></li>' % (1)
page_html_list.append(first_page)
if self.current_page <= 1:
prev_page = '<li class="disabled"><a href="#">上一页</a></li>'
else:
prev_page = '<li><a href="?page=%s">上一页</a></li>' % (self.current_page - 1,)
page_html_list.append(prev_page)
for i in range(pager_start, pager_end):
if i == self.current_page:
temp = '<li class="active"><a href="?page=%s">%s</a></li>' % (i, i,)
else:
temp = '<li><a href="?page=%s">%s</a></li>' % (i, i,)
page_html_list.append(temp)
if self.current_page >= self.all_pager:
next_page = '<li class="disabled"><a href="#">下一页</a></li>'
else:
next_page = '<li><a href="?page=%s">下一页</a></li>' % (self.current_page + 1,)
page_html_list.append(next_page)
last_page = '<li><a href="?page=%s">尾页</a></li>' % (self.all_pager,)
page_html_list.append(last_page)
# 尾部添加标签
page_html_list.append('''
</nav>
</ul>
''')
return ''.join(page_html_list)
2、views
def book_list(request):
# 查询图书列表数据
# book_queryset = models.Book.objects.all()
# print(book_queryset)
# <QuerySet [<Book: Book object (1)>, <Book: Book object (4)>, <Book: Book object (6)>, <Book: Book object (7)>, <Book: Book object (8)>, <Book: Book object (9)>, <Book: Book object (10)>, <Book: Book object (11)>]>
# Book object (1) 为id号,数据删除后,id不翟按照顺序,顺序在增大,删掉的id不再存在
current_page = request.GET.get('page')
try:
current_page = int(current_page)
except Exception:
current_page = 1
book_list = models.Book.objects.all()
all_count = book_list.count()
page_pbj = Pagination(current_page, all_count, per_page_num=4)
book_list = book_list[page_pbj.start:page_pbj.end]
page_html = page_pbj.page_html()
return render(request, 'book_list.html', locals())
3、html
{% extends 'home.html' %}
{% block content %}
<h1 class="text-center">图书列表展示</h1>
<a href="/book_add/" class="btn btn-info">添加图书</a>
<table class="table table-striped table-hover">
<thead>
<tr>
<th>标题</th>
<th>价格</th>
<th>出版日期</th>
<th>出版社</th>
<th>作者</th>
<th>操作</th>
</tr>
</thead>
<tbody>
{% for foo in book_list %}
<tr class="tr_{{ foo.pk }}">
<td>{{ foo.title }}</td>
<td>{{ foo.price }}</td>
<td>{{ foo.publish_date|date:'Y-m-d' }}</td>
<td>{{ foo.publish.name }}</td>
{#书查出版社,正向查询,外键字段跳表#}
<td>
{% for author in foo.authors.all %}
{% if forloop.last %}
{{ author.name }}
{% else %}
{{ author.name }} |
{% endif %}
{% endfor %}
</td>
<td>
{# <a href="/book/edit/{{ foo.pk }}" class="btn btn-success">修改</a>#}
<a href="/book_edit/?id={{ foo.pk }}" class="btn btn-success">修改</a>
<a href="#" class="del btn btn-danger" delete_id="{{ foo.pk }}">删除</a>
{#这里不能使用id标签,因为在for循环中,id不能重复 自定义一个id,a标签自动跳转也相当于有二次提交#}
</td>
</tr>
{% endfor %}
</tbody>
</table>
{% endblock %}
{% block js %}
<script>
$(".del").click(function () {
// 删除的逻辑:当我们点击删除按钮的时候,应该获取点击行的id值,然后,把这个id传到后端,后端接收这个id值
// 做删除逻辑
var id = $(this).attr('delete_id');
{#这里的this代表的是$(".btn")对象#}
var _this = $(this);
// 紧接着要发送ajax请求,最好做一个二次确认
layer.confirm('你确定要删除这条数据吗?', {
btn: ['确定'] //按钮
}, function () {
// 发送ajax请求
$.ajax({
url: '/book_del/', // {# 把请求提交到del视图函数中去#}
type: 'post',
data: {id: id},
success: function (res) {
if (res.code == 200) {
{#layer.msg(res.msg, {icon:2}, function () {#}
{# location.reload();} {# ajax不会自动刷新页面 #}
layer.msg(res.msg);
{# 接收后端返回的信息 #}
_this.parent().parent().remove();
{# this 指的是function (res) _this引用变量 #}
{#$(".tr_" + id).remove(); 删除dom的tr行来实现不展示#}
}
}
});
});
})
</script>
{% endblock %}
{% block fenye %}
{{ page_html | safe }}
{% endblock %}
4、home页面划分区域

5、效果


浙公网安备 33010602011771号