tornado總結5-登陸與cookies

目標

部分頁面只有登陸成功才能容許訪問,不然自動跳轉到登陸頁面html

 

代碼結構

https://git.oschina.net/donggen/tornado-test.git   分支是 tornado總結5git

 

實際運行效果

1.沒有登陸的狀況下訪問 「http://localhost:8899」,  自動跳轉到了 登陸頁面web

能夠看到地址欄是 http://localhost:8899/login?next=%2Fhomejson

2.輸入錯誤的用戶名或密碼瀏覽器

3.輸入正確的用戶名和密碼cookie

彈窗顯示登陸成功,而且跳轉到了home頁面(這個跳轉是js作的, 若是是get請求能夠使用redirect來進行跳轉)。app

跳轉後的頁面dom

 

 

代碼說明

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,其餘數據是用戶相關信息。async

目前只存儲了一個用戶,用戶id是「1」,用戶名是「admin」,密碼是「123456」,用戶暱稱是「管理員」。ide

 

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返回登陸成功的值。

相關文章
相關標籤/搜索