tornado总结5-登录与cookies

江枫思渺然 提交于 2019-12-21 11:27:59

【推荐】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相关说明

  1. cookie_secret 是供给tornado进行cookie进行加密用的。
  2. 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返回登录成功的值。

 

 

 

 

 

 

 

 

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!