在[上一节](https://github.com/qiwsir/StarterLearningPython/blob/master/307.md)中已经对安全问题进行了描述,另外一个内容是不能忽略的,那就是用户登录之后,对当前用户状态(用户是否登录)进行判断。
## [](https://github.com/qiwsir/StarterLearningPython/blob/master/308.md#用户验证)用户验证
用户登录之后,当翻到别的目录中时,往往需要验证用户是否处于登录状态。当然,一种比较直接的方法,就是在转到每个目录时,都从cookie中把用户信息,然后传到后端,跟数据库验证。这不仅是直接的,也是基本的流程。但是,这个过程如果总让用户自己来做,框架的作用就显不出来了。tornado就提供了一种用户验证方法。
为了后面更工程化地使用tornado编程。需要将前面的已经有的代码进行重新梳理。我只是将有修改的文件代码写出来,不做过多解释,必要的有注释,相信读者在前述学习基础上,能够理解。
在handler目录中增加一个文件,名称是base.py,代码如下:
~~~
#! /usr/bin/env python
# coding=utf-8
import tornado.web
class BaseHandler(tornado.web.RequestHandler):
def get_current_user(self):
return self.get_secure_cookie("user")
~~~
在这个文件中,目前只做一个事情,就是建立一个名为BaseHandler的类,然后在里面放置一个方法,就是得到当前的cookie。在这里特别要向读者说明,在这个类中,其实还可以写不少别的东西,比如你就可以将数据库连接写到这个类的初始化`__init__()`方法中。因为在其它的类中,我们要继承这个类。所以,这样一个架势,就为读者以后的扩展增加了冗余空间。
然后把index.py文件改写为:
~~~
#!/usr/bin/env python
# coding=utf-8
import tornado.escape
import methods.readdb as mrd
from base import BaseHandler
class IndexHandler(BaseHandler): #继承base.py中的类BaseHandler
def get(self):
usernames = mrd.select_columns(table="users",column="username")
one_user = usernames[0][0]
self.render("index.html", user=one_user)
def post(self):
username = self.get_argument("username")
password = self.get_argument("password")
user_infos = mrd.select_table(table="users",column="*",condition="username",value=username)
if user_infos:
db_pwd = user_infos[0][2]
if db_pwd == password:
self.set_current_user(username) #将当前用户名写入cookie,方法见下面
self.write(username)
else:
self.write("-1")
else:
self.write("-1")
def set_current_user(self, user):
if user:
self.set_secure_cookie('user', tornado.escape.json_encode(user)) #注意这里使用了tornado.escape.json_encode()方法
else:
self.clear_cookie("user")
class ErrorHandler(BaseHandler): #增加了一个专门用来显示错误的页面
def get(self): #但是后面不单独讲述,读者可以从源码中理解
self.render("error.html")
~~~
在index.py的类IndexHandler中,继承了BaseHandler类,并且增加了一个方法set_current_user()用于将用户名写入cookie。请读者特别注意那个tornado.escape.json_encode()方法,其功能是:
> tornado.escape.json_encode(value) JSON-encodes the given Python object.
如果要查看源码,可以阅读:[http://www.tornadoweb.org/en/branch2.3/escape.html](http://www.tornadoweb.org/en/branch2.3/escape.html)
这样做的本质是把user转化为json,写入到了cookie中。如果从cookie中把它读出来,使用user的值时,还会用到:
> tornado.escape.json_decode(value) Returns Python objects for the given JSON string
它们与[json模块中的dump()、load()](https://github.com/qiwsir/StarterLearningPython/blob/master/227.md)功能相仿。
接下来要对user.py文件也进行重写:
~~~
#!/usr/bin/env python
# coding=utf-8
import tornado.web
import tornado.escape
import methods.readdb as mrd
from base import BaseHandler
class UserHandler(BaseHandler):
@tornado.web.authenticated
def get(self):
#username = self.get_argument("user")
username = tornado.escape.json_decode(self.current_user)
user_infos = mrd.select_table(table="users",column="*",condition="username",value=username)
self.render("user.html", users = user_infos)
~~~
在get()方法前面添加`@tornado.web.authenticated`,这是一个装饰器,它的作用就是完成tornado的认证功能,即能够得到当前合法用户。在原来的代码中,用`username = self.get_argument("user")`方法,从url中得到当前用户名,现在把它注释掉,改用`self.current_user`,这是和前面的装饰器配合使用的,如果它的值为假,就根据setting中的设置,寻找login_url所指定的目录(请关注下面对setting的配置)。
由于在index.py文件的set_current_user()方法中,是将user值转化为json写入cookie的,这里就得用`username = tornado.escape.json_decode(self.current_user)`解码。得到的username值,可以被用于后一句中的数据库查询。
application.py中的setting也要做相应修改:
~~~
#!/usr/bin/env python
# coding=utf-8
from url import url
import tornado.web
import os
setting = dict(
template_path = os.path.join(os.path.dirname(__file__), "templates"),
static_path = os.path.join(os.path.dirname(__file__), "statics"),
cookie_secret = "bZJc2sWbQLKos6GkHn/VB9oXwQt8S0R0kRvJ5/xJ89E=",
xsrf_cookies = True,
login_url = '/',
)
application = tornado.web.Application(
handlers = url,
**setting
)
~~~
与以前代码的重要区别在于`login_url = '/',`,如果用户不合法,根据这个设置,会返回到首页。当然,如果有单独的登录界面,比如是`/login`,也可以`login_url = '/login'`。
如此完成的是用户登录到网站之后,在页面转换的时候实现用户认证。
为了演示本节的效果,我对教程的源码进行修改。读者在阅读的时候,可以参照源码。
- 第零章 预备
- 关于Python的故事
- 从小工到专家
- Python安装
- 集成开发环境
- 第壹章 基本数据类型
- 数和四则运算
- 除法
- 常用数学函数和运算优先级
- 写一个简单的程序
- 字符串(1)
- 字符串(2)
- 字符串(3)
- 字符串(4)
- 字符编码
- 列表(1)
- 列表(2)
- 列表(3)
- 回顾list和str
- 元组
- 字典(1)
- 字典(2)
- 集合(1)
- 集合(2)
- 第贰章 语句和文件
- 运算符
- 语句(1)
- 语句(2)
- 语句(3)
- 语句(4)
- 语句(5)
- 文件(1)
- 文件(2)
- 迭代
- 练习
- 自省
- 第叁章 函数
- 函数(1)
- 函数(2)
- 函数(3)
- 函数(4)
- 函数练习
- 第肆章 类
- 类(1)
- 类(2)
- 类(3)
- 类(4)
- 类(5)
- 多态和封装
- 特殊方法(1)
- 特殊方法(2)
- 迭代器
- 生成器
- 上下文管理器
- 第伍章 错误和异常
- 错误和异常(1)
- 错误和异常(2)
- 错误和异常(3)
- 第陆章 模块
- 编写模块
- 标准库(1)
- 标准库(2)
- 标准库(3)
- 标准库(4)
- 标准库(5)
- 标准库(6)
- 标准库(7)
- 标准库(8)
- 第三方库
- 第柒章 保存数据
- 将数据存入文件
- mysql数据库(1)
- MySQL数据库(2)
- mongodb数据库(1)
- SQLite数据库
- 电子表格
- 第捌章 用Tornado做网站
- 为做网站而准备
- 分析Hello
- 用tornado做网站(1)
- 用tornado做网站(2)
- 用tornado做网站(3)
- 用tornado做网站(4)
- 用tornado做网站(5)
- 用tornado做网站(6)
- 用tornado做网站(7)
- 第玖章 科学计算
- 为计算做准备
- Pandas使用(1)
- Pandas使用(2)
- 处理股票数据
- 附:网络文摘
- 如何成为Python高手
- ASCII、Unicode、GBK和UTF-8字符编码的区别联系