【推荐】2019 Java 开发者跳槽指南.pdf(吐血整理) >>>
目标
部分页面只有登录成功才能允许访问,否则自动跳转到登录页面
代码结构
https://git.oschina.net/donggen/tornado-test.git 分支是 tornado总结5
实际运行效果
1.没有登录的情况下访问 “http://localhost:8899”, 自动跳转到了 登录页面
可以看到地址栏是 http://localhost:8899/login?next=%2Fhome
2.输入错误的用户名或密码
3.输入正确的用户名和密码
弹窗显示登录成功,并且跳转到了home页面(这个跳转是js做的, 如果是get请求可以使用redirect来进行跳转)。
跳转后的页面
代码说明
main.py
import os
import tornado.httpserver
import tornado.ioloop
import tornado.web
import my_uimodules
from handlers.home import HomeHandler
from handlers.login import LoginHandler
class PageNotFoundHandler(tornado.web.RequestHandler):
def get(self):
return self.write_error(404)
class Application(tornado.web.Application):
def __init__(self):
handlers = [
(r"/", tornado.web.RedirectHandler, {"url": "/home"}),
(r"/login", LoginHandler),
(r"/home", HomeHandler),
(r".*", PageNotFoundHandler),
]
settings = dict(
static_path= os.path.join(os.path.dirname(__file__), "static"),
template_path=os.path.join(os.path.dirname(__file__), "templates"),
ui_modules=my_uimodules,
cookie_secret='1111111111111111111111111111111111111111111111111',
login_url='/login',
debug=True
)
tornado.web.Application.__init__(self, handlers, **settings)
self.user_list = {
'1': {'login': 'admin', 'password': '123456', 'name': '管理员'}
}
if __name__ == "__main__":
port = 8899
application = Application()
http_server = tornado.httpserver.HTTPServer(application, xheaders=True)
http_server.listen(port)
print('Listen on http://localhost:{0}'.format(port))
tornado.ioloop.IOLoop.instance().start()
settings相关说明
- cookie_secret 是供给tornado进行cookie进行加密用的。
- login_url 设置登录路径,当某个页面需要进行登录验证,并且发现验证失败时,tornado会通知浏览器跳转到这个路径。
可登录的用户
用户列表被定义成一个字典存储在变量self.user_list里面,用户id是key,其他数据是用户相关信息。
目前只存储了一个用户,用户id是“1”,用户名是“admin”,密码是“123456”,用户昵称是“管理员”。
handlers的 __init__.py文件
import binascii
import tornado.web
import os
class BaseHandler(tornado.web.RequestHandler):
# 类的静态变量用于保存登录信息, 存储的是token对应的user_id
__TOKEN_LIST = {}
def __init__(self, application, request, **kwargs):
super(BaseHandler, self).__init__(application, request, **kwargs)
def new_token(self):
while True:
new_token = binascii.hexlify(os.urandom(16)).decode("utf8")
if new_token not in self.__TOKEN_LIST:
return new_token
def on_login_success(self, new_token, user_id):
self.set_cookie('_token', new_token)
self.__TOKEN_LIST[new_token] = user_id
def get_current_user(self):
# 从cookie中读取token
token = self.get_cookie('_token')
# 根据token得到绑定的用户id
if token and token in self.__TOKEN_LIST:
user_id = self.__TOKEN_LIST[token]
return self.application.user_list[user_id]
# 没有找到就返回none, 表示该用户没有登录
return None
定义了一个BaseHandler类,该类将会被所有需要登录才能访问的页面继承
__TOKEN_LIST 这个被定义成类的静态变量,用于存储登录成功时的_token以及对应的用户id。
on_login_success 是把登录成功的token放入__TOKEN_LIST。
get_current_user 这个方法是登录判断的关键,tornado要求登录判断必须提供这个方法,一般返回 已登录的用户信息, 如果返回None表示用户没有登录。 我这边的判断方法是获取当前请求的cookie,判断里面的_token是否在__TOKEN_LIST里面,如果有就返回对应用户的相关信息。
用户验证的current_user 说明
以下代码是 tornado.web.RequestHandler current_user属性的源代码,current_user这个属性是用来进行用户权限验证, 对于这个属性的处理有两种方法,其中一种方法是子类重写get_current_user, 我这里使用的就是这个。
@property
def current_user(self):
"""The authenticated user for this request.
This is set in one of two ways:
* A subclass may override `get_current_user()`, which will be called
automatically the first time ``self.current_user`` is accessed.
`get_current_user()` will only be called once per request,
and is cached for future access::
def get_current_user(self):
user_cookie = self.get_secure_cookie("user")
if user_cookie:
return json.loads(user_cookie)
return None
* It may be set as a normal variable, typically from an overridden
`prepare()`::
@gen.coroutine
def prepare(self):
user_id_cookie = self.get_secure_cookie("user_id")
if user_id_cookie:
self.current_user = yield load_user(user_id_cookie)
Note that `prepare()` may be a coroutine while `get_current_user()`
may not, so the latter form is necessary if loading the user requires
asynchronous operations.
The user object may any type of the application's choosing.
"""
if not hasattr(self, "_current_user"):
self._current_user = self.get_current_user()
return self._current_user
home.py
import tornado.web
from handlers import BaseHandler
class HomeHandler(BaseHandler):
@tornado.web.authenticated
def get(self):
user_info = self.current_user
return self.render('home.html', user_info=user_info)
HomeHandler 的get方法有个装饰器 @tornado.web.authenticated,该装饰器是用来告知tornado处理HomeHandler的get请求时,需要判断用户是否已登录,如果没有就会跳转到login_url, 如果是post方法机会返回HTTP代码 403。
login.py
import json
from handlers import BaseHandler
class LoginHandler(BaseHandler):
def get(self):
return self.render('login.html')
def post(self):
# 从post的body中获取登录信息
requ_data = json.loads(self.request.body.decode())
login = requ_data['login']
password = requ_data['password']
# 检测用户名和密码
login_user_id = None
for user_id in self.application.user_list:
if login == self.application.user_list[user_id]['login']:
login_user_id = user_id
break
if not login_user_id:
return self.finish('用户名或密码错误')
if password != self.application.user_list[login_user_id]['password']:
return self.finish('用户名或密码错误')
# 一个token对应一个已经登录的用户
new_token = self.new_token()
self.on_login_success(new_token, login_user_id)
return self.finish('ok')
前面一大段代码是获取请求里面的用户名和密码,然后进行登录判断,如果成功就会设置一个 "_token" 的cookie, 并且保存登录状态,设置cookie这个动作是在 BaseHandler的 on_login_success方法里面完成的。
其他代码
略,可以去git上面查看。
简单流程图
事实上没有设置current_user属性这个动作, 而是让get_current_user返回登录成功的值。
来源:oschina
链接:https://my.oschina.net/u/111188/blog/673351