部分頁面只有登陸成功才能容許訪問,不然自動跳轉到登陸頁面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
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()
用戶列表被定義成一個字典存儲在變量self.user_list裏面,用戶id是key,其餘數據是用戶相關信息。async
目前只存儲了一個用戶,用戶id是「1」,用戶名是「admin」,密碼是「123456」,用戶暱稱是「管理員」。ide
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裏面,若是有就返回對應用戶的相關信息。
如下代碼是 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
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。
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返回登陸成功的值。