在Web服务中会有用户登录后的一系列操作, 如果一个客户端的http
请求要求是用户登录后才能做得操作, 那么 Web服务器接收请求时
需要判断该请求里带的数据是否有用户认证的信息.

使用Tornado框架开发Web服务, 框架里提供了tornado.web.authenticated
的 decorator 的辅助开发者做用户登录认证, 即开发者在实现一个 handler
(对应一个url资源, 继承于tornado.web.RequestHandler)时,
该 url的资源操作需要有用户认证或者登录为前提, 那么在资源请求的方法
覆写时(overwritten), 例如在 get 与 post 方法定义前以
tornado.web.authenticated 装饰,并且同时覆写 get_current_user
方法(RequestHandler只是定义空函数, 默认放回None). 在覆写之后,
RequestHandler 类的实例里 current_user 就会有值. current_user
在 tornado源码中是 getter setter的实现, 真正的成员变量是 _current_user
(稍后解析tornado里的源码). authenticated 即实现了 current_user 判断
这一过程来验证用户.

先来看简单的例子(已添加注释 代码来自中文文档):

不使用 tornado.web.authenticated, 直接判断 current_user 成员

 
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
# 简单的用户认证实现
 
# BaseHandler 基类覆写 get_current_user
# 覆写后 RequestHandler 的current_user成员会有值(稍后解释实现源码)
# 这里简单地判断请求带的 secure cookie 是否带有 user属性的值
class BaseHandler(tornado.web.RequestHandler):
    def get_current_user(self):
        return self.get_secure_cookie("user")
 
# 实际业务类实现
class MainHandler(BaseHandler):
    def get(self):
        # 判断 current_user, 如果不存在值,要求重定向到 login页面
        if not self.current_user:
            self.redirect("/login")
            return
        name = tornado.escape.xhtml_escape(self.current_user)
        self.write("Hello, " + name)
 
class LoginHandler(BaseHandler):
    def get(self):
        self.write('<html><body><form action="/login" method="post">'
                   'Name: <input type="text" name="name">'
                   '<input type="submit" value="Sign in">'
                   '</form></body></html>')
 
    def post(self):
        self.set_secure_cookie("user", self.get_argument("name"))
        self.redirect("/")
 
application = tornado.web.Application([
    (r"/", MainHandler),
    (r"/login", LoginHandler),
], cookie_secret="61oETzKXQAGaYdkL5gEmGeJJFuYh7EQnp2XdTP1o/Vo="
 
 
 

在 Get 方法上添加 authenticated 装饰器实现用户认证:

 
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
# 使用装饰器实现用户认证
 
class MainHandler(BaseHandler):
    @tornado.web.authenticated
    def get(self):
        """
        直接写业务逻辑代码, 方法中不必考虑多写一份判断
        代码少即是多的原则
        """
        name = tornado.escape.xhtml_escape(self.current_user)
        self.write("Hello, " + name)
 
# cookie_secret 是用于 secure_cookie 加密实现的
settings = {
    "cookie_secret": "61oETzKXQAGaYdkL5gEmGeJJFuYh7EQnp2XdTP1o/Vo=",
    "login_url": "/login",
}  
 
application = tornado.web.Application([
    (r"/", MainHandler),
    (r"/login", LoginHandler),
], **settings) #**  
 
 

看完实现的小例子, 就要探究其 decorator 的实现细节:
以知晓 tornado 为何可以辅助开发者更方便实现用户认证
源码版本 tornado 4.0.2 tornado/web.py (已添加注释):

 
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
# RequestHandler current_user 与 authenticated实现细节
 
class RequestHandler(object):
    """    
    property 装饰器将 current_user 设置为 getter 方法.
    即 handler.current_user 可以当作类数据成员的方式书写使用
    不需要以方法书写 
    """
    @property
    def current_user(self):
        """The authenticated user for this request.
 
 
        This is a cached version of `get_current_user`, which you can
        override to set the user based on, e.g., a cookie. If that
        method is not overridden, this method always returns None.
 
 
        We lazy-load the current user the first time this method is called
        and cache the result after that.
        """
        """
        延迟(lazy)方式加载 _current_user值,
        即从 get_current_user()方法中获取值,
        因此 get_current_user 需要开发者自己覆写内容.  
        """
        if not hasattr(self, "_current_user"):
            self._current_user = self.get_current_user()
        return self._current_user
 
    @current_user.setter
    def current_user(self, value):
        self._current_user = value
 
    def get_current_user(self):
        """
        默认返回 None,
        之前的 BaseHandler 的样例代码覆写判断逻辑时
        使用的是 cookie 是否存在 user 属性作为判断
        """
 
        """Override to determine the current user from, e.g., a cookie."""
        return None  
 
# authenticated 装饰器
def authenticated(method):
    """Decorate methods with this to require that the user be logged in.
 
    If the user is not logged in, they will be redirected to the configured
    `login url <RequestHandler.get_login_url>`.
 
    If you configure a login url with a query parameter, Tornado will
    assume you know what you're doing and use it as-is.  If not, it
    will add a `next` parameter so the login page knows where to send
    you once you're logged in.
    """
    @functools.wraps(method)
    def wrapper(self, *args, **kwargs):
        """
        这里调用的是 current_user 的 get 方法(property装饰),
        紧接着调用 return self._current_user
        原本放在业务逻辑代码中做的判断, 现在交给 decorator 帮助  
        开发者, 开发者可以少写代码, 专注自己的业务
        """
        if not self.current_user:    
            if self.request.method in ("GET", "HEAD"):
                url = self.get_login_url()
                if "?" not in url:
                    if urlparse.urlsplit(url).scheme:
                        # if login url is absolute, make next absolute too
                        next_url = self.request.full_url()
                    else:
                        next_url = self.request.uri
                    url += "?" + urlencode(dict(next=next_url))
                self.redirect(url)
                return
            raise HTTPError(403)
        return method(self, *args, **kwargs)
    return wrapper  
 
 

这里我们要理解的是 authenticated 装饰器的用法, 继承于
RequestHandler 的 handler 类, 开发者覆写 get post 方法
实现时, 如果要判断请求的合理性(即用户是否被认证过), 可
以在覆写方法里业务代码前加上判断代码, 这样也可以实现
同样的功能, 而 Tornado 利用了Python的语言特性, 将用户
认证的代码通过 decorator “桥接” 完成, 即 get post 这些 http
请求方法里的代码可以保持功能的专注度. 此外, 如果开发
需求更改, 资源请求不需要用户认证时, 可直接注释或者删除
方法上方的 decorator 即可, 方便快捷省事:).

用户认证未通过的重定向设置

当用户没有认证通过时, 可以在程序入口, 设置 settings dict 属性,
设置 login_url 属性 参考文档
“””
login_url: The authenticated decorator will redirect to this url
if the user is not logged in. Can be further customized
by overriding RequestHandler.get_login_url
“””
样例:

 
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
# settings 属性设置
 
settings = dict(
# ...
login_url = "/login",
# ...
)
 
"""
tornado.web.authenticated 未通过时, 默认  
redirect 到 "/login"  
"""
application = tornado.web.Application(handlers, **settings)  
 
 

用户认证在什么场景下使用:

我们通常的业务需求中, 会涉及到 session会话保持 与 cookie 的
用户数据的读取场景, 即从 http 请求的 cookie 中读取 sessionid,
以 sessionid 为 key, 从内存或者缓存中判断 sessionid 是否存在值,
以此作为用户登录状态的认证, 或者是用户重新打开浏览器, 之前
浏览器缓存的cookie里的sessionid重新发送给客户端, 用户无需
重新输入账号密码, 即可直接在登录状态. 较前两年基于 memcache
做服务端 session 的缓存, 现在可以使用 Redis 服务替代 memcache,
做缓存数据库的工作.

Tornado web.authenticated 用户认证浅析的更多相关文章

  1. Web Api2 用户认证模板解析---本地用户

    本文我们将把关注Visual Studio用户认证模板中的 本地用户名/密码帐号特性.他们将其分为了两部分:帐户控制器具备如创建帐号和修改密码等功能:另一部分是在OAuth2认证服务器中进行的认证.我 ...

  2. 浅析tornado web框架

    tornado简介 1.tornado概述 Tornado就是我们在 FriendFeed 的 Web 服务器及其常用工具的开源版本.Tornado 和现在的主流 Web 服务器框架(包括大多数 Py ...

  3. 使用tornado实现用户认证

    关于用户的登录状态,一部分的应用程序是采用session实现的. HTTP是一个无状态协议,用户的每次请求都是相互独立的,HTTP本身意识不到用户是否登录. 很多web框架选择将session存放在c ...

  4. tornado安全应用之用户认证

    在这个例子中,我们将只通过存储在安全cookie里的用户名标识一个人.当某人首次在某个浏览器(或cookie过期后)访问我们的页面时,我们展示一个登录表单页面.表单作为到LoginHandler路由的 ...

  5. Python(九)Tornado web 框架

    一.简介 Tornado 是 FriendFeed 使用的可扩展的非阻塞式 web 服务器及其相关工具的开源版本.这个 Web 框架看起来有些像web.py 或者 Google 的 webapp,不过 ...

  6. Tornado web 框架

    Tornado web 框架 其实很简单.深度应用 一.简介 Tornado 是 FriendFeed 使用的可扩展的非阻塞式 web 服务器及其相关工具的开源版本.这个 Web 框架看起来有些像we ...

  7. 4.(基础)tornado应用安全与认证

    这一节我们介绍应用安全与认证,其实中间省略了一个数据库.对于tornado来说,读取数据库的数据,性能的瓶颈还是在数据库上面.关于数据库,我在<>中介绍了sqlalchemy,这是一个工业 ...

  8. Laravel 5 中使用 JWT(Json Web Token) 实现基于API的用户认证

    在JavaScript前端技术大行其道的今天,我们通常只需在后台构建API提供给前端调用,并且后端仅仅设计为给前端移动App调用.用户认证是Web应用的重要组成部分,基于API的用户认证有两个最佳解决 ...

  9. Web网站实现用户认证访问(加密访问)

    Web网站实现用户认证访问,有效减少流量的访问,具体的实现步骤如下: 我们使用httpd作为测试对象,体现安装好httpd服务,并且可以在浏览器访问测试首页(可以关闭防火墙:如果不关闭防火墙,则需要开 ...

随机推荐

  1. Apache Kafka:下一代分布式消息系统

    [http://www.infoq.com/cn/articles/apache-kafka/]分布式发布-订阅消息系统. Kafka是一种快速.可扩展的.设计内在就是分布式的,分区的和可复制的提交日 ...

  2. css 精灵的用法

    (从已经死了一次又一次终于挂掉的百度空间人工抢救出来的,发表日期2014-02-12) 是指将多个图整合到一张图上,避免多次请求服务器下载. 主要使用CSS background-position 属 ...

  3. Instant Buy Android API Tutorial

    转自:https://developers.google.com/wallet/instant-buy/android/tutorial This tutorial guides you throug ...

  4. Netsh配置端口

    开启防火墙端口 netsh firewall set portopening all 3389 RDP_Rule enable (TCP/UDP) #配置RPC高位端口段Netsh int ipv4 ...

  5. c# ActiveX 控件的开发

    关于ActiveX控件的开发,网上很多例子,昨天也整整研究一天才捋顺了. 网上大部分例子都是js调用控件的方法,由于要实现在html页面"相应"控件的事件,整整折腾一天. 关键点在 ...

  6. cocos2d-x 3.1.1 学习笔记[3]Action 动作

    这些动画貌似都非常多的样子,就所有都创建一次. 代码例如以下: /* 动画*/ auto sp = Sprite::create("card_bg_big_26.jpg"); Si ...

  7. 【JavsScript】推荐五款流行的JavaScript模板引擎

    摘要:Javascript模板引擎作为数据与界面分离工作中最重要一环,受到开发者广泛关注.本文通过开发实例解析五款流行模板引擎:Mustache.Underscore Templates.Embedd ...

  8. QoS 测量 (目标,方法,协议)

    本文翻译自ITU-T的Technical Paper:<How to increase QoS/QoE of IP-based platform(s) to regionally agreed ...

  9. Java Web模块——验证码模块

    一.什么是验证码及它的作用 验 证码为全自动区分计算机和人类的图灵测试的缩写,是一种区分用户是计算机的公共全自动程序,这个问题可以由计算机生成并评判,但是必须只有人类才能解答. 可以防止恶意破解密码. ...

  10. 云服务器 ECS Linux 系统添加“回收站”

    删除是危险系数很高的操作,一旦误删可能会造成难以估计的损失.在云服务器 ECS Linux 系统中这种危险尤为明显.比如,一条简单的语句:rm –rf /* 就会把整个系统全部删除,而 Linux 并 ...