Skip to content

Commit

Permalink
更新了文档和交流群信息
Browse files Browse the repository at this point in the history
  • Loading branch information
jackfrued committed May 25, 2019
1 parent 43aa496 commit 5bd10ce
Show file tree
Hide file tree
Showing 5 changed files with 173 additions and 11 deletions.
Binary file modified .DS_Store
Binary file not shown.
123 changes: 120 additions & 3 deletions Day41-55/05.Cookie和Session.md
Original file line number Diff line number Diff line change
Expand Up @@ -9,12 +9,129 @@
要让客户端记住并在每次请求时带上sessionid又有以下几种做法:

1. URL重写。所谓URL重写就是在URL中携带sessionid,例如:`http://www.example.com/index.html?sessionid=123456`,服务器通过获取sessionid参数的值来取到与之对应的session对象。

2. 隐藏域(隐式表单域)。在提交表单的时候,可以通过在表单中设置隐藏域向服务器发送额外的数据。例如:`<input type="hidden" name="sessionid" value="123456">`
3. Cookie。Cookie是保存在浏览器临时文件中的数据,每次请求时,请求头中会携带本站点的Cookie到服务器,那么只要将sessionid写入cookie,下次请求时服务器就能够获得这个sessionid。

需要说明的是,在HTML5时代要想在浏览器中保存数据,除了使用Cookie之外,还可以使用新的本地存储API,包括localStorage、sessionStorage、IndexedDB等,如下图所示。
3. Cookie。Cookie是保存在浏览器临时文件中的数据,每次请求时,请求头中会携带本站点的cookie到服务器,那么只要将sessionid写入cookie,下次请求时服务器只要读取请求头中的cookie就能够获得这个sessionid,如下图所示:

![](./res/sessionid_from_cookie.png)

需要说明的是,在HTML5时代要想在浏览器中保存数据,除了使用cookie之外,还可以使用新的本地存储API,包括localStorage、sessionStorage、IndexedDB等,如下图所示。

![](./res/cookie_xstorage_indexeddb.png)

### Django框架对Session的支持
### Django框架对session的支持

在创建Django项目时,默认的配置文件`settings.py`文件中已经激活了一个名为`SessionMiddleware`的中间件(关于中间件的知识我们在下一个章节做详细的讲解,这里只需要知道它的存在即可),因为这个中间件的存在,我们可以直接通过请求对象的`session`属性来操作会话对象。`session`属性是一个像字典一样可以读写数据的容器对象,因此我们可以使用“键值对”的方式来保留用户数据。与此同时,`SessionMiddleware`中间件还封装了对cookie的操作,在cookie中保存了sessionid,就如同我们之前描述的那样。

在默认情况下,Django将session的数据序列化后保存在关系型数据库中,在Django 1.6以后的版本中,默认的序列化数据的方式是JSON序列化,而在此之前一直使用Pickle序列化。JSON序列化和Pickle序列化的差别在于前者将对象序列化为字符串(字符形式),而后者将对象序列化为字节串(二进制形式),因为安全方面的原因,JSON序列化成为了目前Django框架默认序列化数据的方式,这就要求在我们保存在session中的数据必须是能够JSON序列化的,否则就会引发异常。还有一点需要说明的是,使用关系型数据库保存session中的数据在大多数时候并不是最好的选择,因为数据库可能会承受巨大的压力而成为系统性能的瓶颈,在后面的章节中我们会告诉大家如何将session的数据保存到缓存服务中。

我们继续完善之前的投票应用,前一个章节中我们实现了用户的登录和注册,下面我们首先完善登录时对验证码的检查。

```Python
def get_captcha(request):
"""验证码"""
captcha_text = random_captcha_text()
request.session['captcha'] = captcha_text
image_data = Captcha.instance().generate(captcha_text)
return HttpResponse(image_data, content_type='image/png')
```

注意上面代码中的第4行,我们将随机生成的验证码字符串保存到session中,稍后用户登录时,我们要将保存在session中的验证码字符串和用户输入的验证码字符串进行比对,如果用户输入了正确的验证码才能够执行后续的登录流程,代码如下所示。

```Python
def login(request: HttpRequest):
"""登录"""
hint = ''
if request.method == 'POST':
form = LoginForm(request.POST)
if form.is_valid():
# 对验证码的正确性进行验证
captcha_from_user = form.cleaned_data['captcha']
captcha_from_sess = request.session.get('captcha', '')
if captcha_from_sess.lower() != captcha_from_user.lower():
hint = '请输入正确的验证码'
else:
username = form.cleaned_data['username']
password = form.cleaned_data['password']
user = User.objects.filter(username=username, password=password).first()
if user:
# 登录成功后将用户编号和用户名保存在session中
request.session['no'] = user.no
request.session['username'] = user.username
return redirect('/')
else:
hint = '用户名或密码错误'
else:
hint = '请输入有效的登录信息'
return render(request, 'login.html', {'hint': hint})
```

上面的代码中,我们设定了登录成功后会在session中保存用户的编号(`no`)和用户名(`username`),页面会重定向到首页。接下来我们可以稍微对首页的代码进行调整,在页面的右上角显示出登录用户的用户名。我们将这段代码单独写成了一个名为header.html的HTML文件,首页中可以通过在`<body>`标签中添加`{% include 'header.html' %}`来包含这个页面,代码如下所示。

```HTML
<div class="user">
{% if request.session.no %}
<span>{{ request.session.username }}</span>
<a href="/vote/logout">注销</a>
{% else %}
<a href="/vote/login">登录</a>&nbsp;&nbsp;
{% endif %}
<a href="/vote/register">注册</a>
</div>
```

如果用户没有登录,页面会显示登录和注册的超链接;而用户登录成功后,页面上会显示用户名和注销的链接,注销链接对应的视图函数如下所示。

```Python
def logout(request):
"""注销"""
request.session.flush()
return redirect('/')
```

上面的代码通过session对象`flush`方法来销毁session,一方面清除了服务器上session对象保存的用户数据,一方面将保存在浏览器cookie中的sessionid删除掉,稍后我们会对如何读写cookie的操作加以说明。

我们可以通过项目使用的数据库中名为`django_session` 的表来找到所有的session,该表的结构如下所示:

| session_key | session_data | expire_date |
| -------------------------------- | ------------------------------- | -------------------------- |
| c9g2gt5cxo0k2evykgpejhic5ae7bfpl | MmI4YzViYjJhOGMyMDJkY2M5Yzg3... | 2019-05-25 23:16:13.898522 |

其中,第1列就是浏览器cookie中保存的sessionid;第2列是经过BASE64编码后的session中的数据,如果使用Python的`base64`对其进行解码,解码的过程和结果如下所示。

```Python
>>> import base64
>>> base64.b64decode('MmI4YzViYjJhOGMyMDJkY2M5Yzg3ZWIyZGViZmUzYmYxNzdlNDdmZjp7ImNhcHRjaGEiOiJzS3d0Iiwibm8iOjEsInVzZXJuYW1lIjoiamFja2ZydWVkIn0=')
'2b8c5bb2a8c202dcc9c87eb2debfe3bf177e47ff:{"captcha":"sKwt","no":1,"username":"jackfrued"}'
```

第3列是session的过期时间,session过期后浏览器保存的cookie中的sessionid就会失效,但是数据库中的这条对应的记录仍然会存在,如果想清除过期的数据,可以使用下面的命令。

```Shell
python manage.py clearsessions
```

Django框架默认的session过期时间为两周(1209600秒),如果想修改这个时间,可以在项目的配置文件中添加如下所示的代码。

```Python
# 配置会话的超时时间为1天(86400秒)
SESSION_COOKIE_AGE = 86400
```

有很多对安全性要求较高的应用都必须在关闭浏览器窗口时让会话过期,不再保留用户的任何信息,如果希望在关闭浏览器窗口时就让会话过期(cookie中的sessionid失效),可以加入如下所示的配置。

```Python
# 设置为True在关闭浏览器窗口时session就过期
SESSION_EXPIRE_AT_BROWSER_CLOSE = True
```

如果不希望将session的数据保存在数据库中,可以将其放入缓存中,对应的配置如下所示,缓存的配置和使用我们在后面讲解。

```Python
# 配置将会话对象放到缓存中存储
SESSION_ENGINE = 'django.contrib.sessions.backends.cache'
# 配置使用哪一组缓存来保存会话
SESSION_CACHE_ALIAS = 'default'
```

Binary file added Day41-55/res/sessionid_from_cookie.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
61 changes: 53 additions & 8 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,9 +2,9 @@

> 作者:骆昊
>
> 说明:最近有很多想学习Python的小伙伴申请单独加我微信,因为我自己平时也很忙,没办法一一解答大家的问题,我创建了一个叫**Python100天学习交流2群**,(之前的**Python100天学习交流群**已满员,新的交流群群号**213132758**,二维码在下方。群里面有我优秀的同事和朋友,我们在有时间的时候会主动为大家解答各种问题,从Python语言入门到Web应用开发,从数据分析到机器学习,每个领域都有该领域的大拿为大家解惑答疑,小伙伴们可以加群进行交流。感谢**千锋教育Python教学部**对本群的支持和赞助
> 说明:最近有很多想学习Python的小伙伴申请单独加我微信和QQ,因为我自己平时也很忙,没办法一一解答大家的问题,我最近创建了**Python100天学习交流3群**,(之前的**Python100天学习交流1群****Python100天学习交流2群**已满员,新的交流群群号**751497128**,二维码在下方。群里面有我优秀的同事和朋友,他们大多数都在Python领域都有着丰富的商业项目经验,我们在有时间的时候会主动为大家解答各种问题,从Python语言入门到Web应用开发,从数据分析到机器学习,每个领域都有技术大咖为大家解惑答疑,以后我们尽量每周做一次视频直播的交流活动,同时还会不定期的举办线上和线下的技术交流和分享活动,小伙伴们可以加群进行交流。感谢**千锋教育Python教学部**对本群的支持
![](./res/python_100_days_qq_group2.png)
![](./res/python_100_days_qq_group3.png)

### Python应用领域和就业形势分析

Expand Down Expand Up @@ -246,33 +246,78 @@

#### Day44 - [表单的应用](./Day41-55/04.表单的应用.md)

- 表单和表单控件
- 跨站请求伪造和CSRF令牌
- Form和ModelForm
- 表单验证

#### Day45 - [Cookie和Session](./Day41-55/05.Cookie和Session.md)

- 实现用户跟踪
- cookie和session的关系
- Django框架对session的支持
- 视图函数中实现cookie读写

#### Day46 - [中间件的应用](./Day41-55/06.中间件的应用.md)

#### Day47 - [日志和缓存](./Day41-55/07.日志和缓存.md)
- 什么是中间件
- Django框架内置的中间件
- 自定义中间件及其应用场景

#### Day47 - [日志和调试](./Day41-55/07.日志和调试.md)

- 配置日志
- 配置和使用Django-Debug-Toolbar

#### Day48 - [文件上传和富文本编辑](./Day41-55/08.文件上传.md)

- 文件上传表单控件和图片文件预览
- 服务器端如何处理上传的文件
- 富文本编辑器概述
- wangEditor的使用

#### Day49 - [文件下载和报表](./Day41-55/09.文件下载和报表.md)

- 通过HttpResponse修改响应头
- 使用StreamingHttpResponse处理大文件
- 使用xlwt生成Excel报表
- 使用reportlab生成PDF报表
- 使用ECharts生成前端图表

#### Day50 - [RESTful架构和DRF入门](./Day41-55/10.RESTful架构和DRF入门.md)

#### Day51 - [RESTful架构和DRF进阶](./Day41-55/11.RESTful架构和DRF进阶.md)

#### Day52 - [使用缓存](./Day41-55/12.使用缓存.md)

- 网站优化第一定律

- 在Django项目中使用Redis提供缓存服务
- 在视图函数中读写缓存
- 使用装饰器实现页面缓存
- 为数据接口提供缓存服务

#### Day53 - [短信和邮件](./Day41-55/13.短信和邮件.md)

- 常用短信网关平台介绍
- 使用螺丝帽发送短信
- Django框架对邮件服务的支持

#### Day54 - [异步任务和定时任务](./Day41-55/14.异步任务和定时任务.md)

- 网站优化第二定律
- 配置消息队列服务
- 在项目中使用celery实现任务异步化
- 在项目中使用celery实现定时任务

#### Day55 - [单元测试和项目上线](./Day41-55/15.单元测试和项目上线.md)

- 项目开发流程和相关工具
- 生成非HTML内容
- 项目部署和测试
- 项目性能初步调优
- Web应用安全保护
- Python中的单元测试
- Django框架对单元测试的支持
- 使用版本控制系统
- 配置和使用uWSGI
- 动静分离和Nginx配置
- 配置HTTPS


### Day56~60 - [实战Flask](./Day56-65)
Expand Down
Binary file added res/python_100_days_qq_group3.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.

0 comments on commit 5bd10ce

Please sign in to comment.