tornado.auth — 使用 OpenID 和 OAuth 进行第三方登录

该模块包含各种第三方身份验证方案的实现。

该文件中的所有类都是类混合,旨在与 tornado.web.RequestHandler 类一起使用。它们以两种方式使用

  • 在登录处理程序中,使用 authenticate_redirect()authorize_redirect()get_authenticated_user() 等方法来建立用户的身份,并将身份验证令牌存储到您的数据库和/或 Cookie 中。

  • 在非登录处理程序中,使用 facebook_request()twitter_request() 等方法使用身份验证令牌向相应的服务发出请求。

由于所有这些服务都以稍微不同的方式实现身份验证和授权,因此它们都采用略有不同的参数。有关完整文档,请参见下面的各个服务类。

Google OAuth 的示例用法

class GoogleOAuth2LoginHandler(tornado.web.RequestHandler,
                                tornado.auth.GoogleOAuth2Mixin):
    async def get(self):
        # Google requires an exact match for redirect_uri, so it's
        # best to get it from your app configuration instead of from
        # self.request.full_uri().
        redirect_uri = urllib.parse.urljoin(self.application.settings['redirect_base_uri'],
            self.reverse_url('google_oauth'))
        async def get(self):
            if self.get_argument('code', False):
                access = await self.get_authenticated_user(
                    redirect_uri=redirect_uri,
                    code=self.get_argument('code'))
                user = await self.oauth2_request(
                    "https://www.googleapis.com/oauth2/v1/userinfo",
                    access_token=access["access_token"])
                # Save the user and access token. For example:
                user_cookie = dict(id=user["id"], access_token=access["access_token"])
                self.set_signed_cookie("user", json.dumps(user_cookie))
                self.redirect("/")
            else:
                self.authorize_redirect(
                    redirect_uri=redirect_uri,
                    client_id=self.get_google_oauth_settings()['key'],
                    scope=['profile', 'email'],
                    response_type='code',
                    extra_params={'approval_prompt': 'auto'})

常见协议

这些类实现了 OpenID 和 OAuth 标准。它们通常需要进行子类化才能与任何特定站点一起使用。所需的自定义程度会有所不同,但在大多数情况下,覆盖类属性(出于历史原因,它们以下划线开头)就足够了。

class tornado.auth.OpenIdMixin[source]

OpenID 和属性交换的抽象实现。

类属性

  • _OPENID_ENDPOINT:身份提供者的 URI。

authenticate_redirect(callback_uri: Optional[str] = None, ax_attrs: List[str] = ['name', 'email', 'language', 'username']) None[source]

重定向到此服务的身份验证 URL。

身份验证后,服务将重定向回给定的回调 URI,并带有其他参数,包括 openid.mode

默认情况下,我们为经过身份验证的用户请求给定的属性(姓名、电子邮件、语言和用户名)。如果您不需要应用程序的所有这些属性,可以使用 ax_attrs 关键字参数请求更少的属性。

在版本 6.0 中更改: 删除了 callback 参数,并且该方法不再返回可等待对象。它现在是一个普通的同步函数。

coroutine get_authenticated_user(http_client: Optional[AsyncHTTPClient] = None) Dict[str, Any][source]

重定向后获取经过身份验证的用户数据。

此方法应由接收来自 authenticate_redirect() 方法的重定向的处理程序调用(这通常与调用它的处理程序相同;在这种情况下,如果 openid.mode 参数存在,您将调用 get_authenticated_user,如果不存在,则调用 authenticate_redirect)。

此方法的结果通常用于设置 Cookie。

在版本 6.0 中更改: 删除了 callback 参数。使用返回的可等待对象代替。

get_auth_http_client() AsyncHTTPClient[source]

返回要用于身份验证请求的 AsyncHTTPClient 实例。

子类可以覆盖此方法以使用除默认 HTTP 客户端之外的其他 HTTP 客户端。

class tornado.auth.OAuthMixin[source]

OAuth 1.0 和 1.0a 的抽象实现。

有关示例实现,请参见下面的 TwitterMixin

类属性

  • _OAUTH_AUTHORIZE_URL:服务的 OAuth 授权 URL。

  • _OAUTH_ACCESS_TOKEN_URL:服务的 OAuth 访问令牌 URL。

  • _OAUTH_VERSION:可以是“1.0”或“1.0a”。

  • _OAUTH_NO_CALLBACKS:如果服务需要提前注册回调,则将其设置为 True。

子类还必须覆盖 _oauth_get_user_future_oauth_consumer_token 方法。

async authorize_redirect(callback_uri: Optional[str] = None, extra_params: Optional[Dict[str, Any]] = None, http_client: Optional[AsyncHTTPClient] = None) None[source]

将用户重定向以获取此服务的 OAuth 授权。

如果您之前已向第三方服务注册了回调 URI,则可以省略 callback_uri。对于某些服务,您必须使用之前注册的回调 URI,并且无法通过此方法指定回调。

此方法设置一个名为 _oauth_request_token 的 cookie,该 cookie 随后在 get_authenticated_user 中用于安全目的(并被清除)。

此方法是异步的,必须使用 awaityield 调用(这与本模块中定义的其他 auth*_redirect 方法不同)。它为您调用 RequestHandler.finish,因此您不应该在它返回后写入任何其他响应。

在 3.1 版本中更改: 现在返回一个 Future 并接受一个可选的回调,以与 gen.coroutine 兼容。

在版本 6.0 中更改: 删除了 callback 参数。使用返回的可等待对象代替。

async get_authenticated_user(http_client: Optional[AsyncHTTPClient] = None) Dict[str, Any][source]

获取 OAuth 授权的用户和访问令牌。

此方法应从您的 OAuth 回调 URL 的处理程序中调用,以完成注册过程。我们使用经过身份验证的用户字典运行回调。此字典将包含一个 access_key,该密钥可用于代表用户向此服务发出授权请求。该字典还将包含其他字段,例如 name,具体取决于所使用的服务。

在版本 6.0 中更改: 删除了 callback 参数。使用返回的可等待对象代替。

_oauth_consumer_token() Dict[str, Any][source]

子类必须重写此方法以返回其 OAuth 消费者密钥。

返回值应为一个 dict,其键为 keysecret

async _oauth_get_user_future(access_token: Dict[str, Any]) Dict[str, Any][source]

子类必须重写此方法以获取有关用户的基本信息。

应为一个协程,其结果是一个字典,其中包含有关用户的的信息,这些信息可能是通过使用 access_token 向服务发出请求而检索到的。

访问令牌将被添加到返回的字典中,以使 get_authenticated_user 的结果。

在 5.1 版本中更改: 子类也可以使用 async def 定义此方法。

在 6.0 版本中更改: 删除了对 _oauth_get_user 的同步回退。

get_auth_http_client() AsyncHTTPClient[source]

返回要用于身份验证请求的 AsyncHTTPClient 实例。

子类可以覆盖此方法以使用除默认 HTTP 客户端之外的其他 HTTP 客户端。

class tornado.auth.OAuth2Mixin[source]

OAuth 2.0 的抽象实现。

有关示例实现,请参见下面的 FacebookGraphMixinGoogleOAuth2Mixin

类属性

  • _OAUTH_AUTHORIZE_URL: 服务的授权 URL。

  • _OAUTH_ACCESS_TOKEN_URL: 服务的访问令牌 URL。

authorize_redirect(redirect_uri: Optional[str] = None, client_id: Optional[str] = None, client_secret: Optional[str] = None, extra_params: Optional[Dict[str, Any]] = None, scope: Optional[List[str]] = None, response_type: str = 'code') None[source]

将用户重定向以获取此服务的 OAuth 授权。

一些提供商要求您在应用程序中注册一个重定向 URL,而不是通过此方法传递。您应该调用此方法来登录用户,然后在重定向 URL 的处理程序中调用 get_authenticated_user 来完成授权过程。

在 6.0 版中变更: The callback argument and returned awaitable were removed; this is now an ordinary synchronous function.

自 6.4 版起已弃用: The client_secret argument (which has never had any effect) is deprecated and will be removed in Tornado 7.0.

coroutine oauth2_request(url: str, access_token: Optional[str] = None, post_args: Optional[Dict[str, Any]] = None, **args: Any) Any[source]

使用 OAuth2 访问令牌获取给定的 URL。

如果请求是 POST,则应提供 post_args。查询字符串参数应作为关键字参数给出。

示例用法

..testcode

class MainHandler(tornado.web.RequestHandler,
                  tornado.auth.FacebookGraphMixin):
    @tornado.web.authenticated
    async def get(self):
        new_entry = await self.oauth2_request(
            "https://graph.facebook.com/me/feed",
            post_args={"message": "I am posting from my Tornado application!"},
            access_token=self.current_user["access_token"])

        if not new_entry:
            # Call failed; perhaps missing permission?
            self.authorize_redirect()
            return
        self.finish("Posted a message!")

在 4.3 版中新增。

get_auth_http_client() AsyncHTTPClient[source]

返回要用于身份验证请求的 AsyncHTTPClient 实例。

子类可以覆盖此方法以使用除默认 HTTP 客户端之外的其他 HTTP 客户端。

在 4.3 版中新增。

Google

class tornado.auth.GoogleOAuth2Mixin[source]

使用 OAuth2 的 Google 身份验证。

为了使用,请在 Google 上注册您的应用程序并将相关参数复制到您的应用程序设置中。

  • 转到 Google 开发者控制台 http://console.developers.google.com

  • 选择一个项目,或创建一个新项目。

  • 根据所需的权限,您可能需要将您的应用程序设置为“测试”模式并将您的帐户添加为测试用户,或通过验证过程。您可能还需要使用“启用 API 和服务”命令来启用特定服务。

  • 在左侧的边栏中,选择凭据。

  • 点击创建凭据,然后点击 OAuth 客户端 ID。

  • 在应用程序类型下,选择 Web 应用程序。

  • 命名 OAuth 2.0 客户端,然后点击创建。

  • 将“客户端密钥”和“客户端 ID”复制到应用程序设置中,作为 {"google_oauth": {"key": CLIENT_ID, "secret": CLIENT_SECRET}}

  • 您必须在凭据页面上注册您打算与该类一起使用的 redirect_uri

在 3.2 版中新增。

get_google_oauth_settings() Dict[str, str][source]

返回您使用 [Google Cloud Platform](https://console.cloud.google.com/apis/credentials) 创建的 Google OAuth 2.0 凭据。字典格式为

{
    "key": "your_client_id", "secret": "your_client_secret"
}

如果您以不同的方式存储凭据(例如在数据库中),您可以重写此方法以进行自定义配置。

coroutine get_authenticated_user(redirect_uri: str, code: str, client_id: Optional[str] = None, client_secret: Optional[str] = None) Dict[str, Any][source]

处理 Google 用户的登录,返回访问令牌。

结果是一个包含 access_token 字段的字典(以及其他字段)([among others](https://developers.google.com/identity/protocols/OAuth2WebServer#handlingtheresponse))。与该包中的其他 get_authenticated_user 方法不同,此方法不返回有关用户的任何其他信息。返回的访问令牌可与 OAuth2Mixin.oauth2_request 一起使用,以请求其他信息(例如,来自 https://www.googleapis.com/oauth2/v2/userinfo)。

示例用法

class GoogleOAuth2LoginHandler(tornado.web.RequestHandler,
                               tornado.auth.GoogleOAuth2Mixin):
    async def get(self):
        # Google requires an exact match for redirect_uri, so it's
        # best to get it from your app configuration instead of from
        # self.request.full_uri().
        redirect_uri = urllib.parse.urljoin(self.application.settings['redirect_base_uri'],
            self.reverse_url('google_oauth'))
        async def get(self):
            if self.get_argument('code', False):
                access = await self.get_authenticated_user(
                    redirect_uri=redirect_uri,
                    code=self.get_argument('code'))
                user = await self.oauth2_request(
                    "https://www.googleapis.com/oauth2/v1/userinfo",
                    access_token=access["access_token"])
                # Save the user and access token. For example:
                user_cookie = dict(id=user["id"], access_token=access["access_token"])
                self.set_signed_cookie("user", json.dumps(user_cookie))
                self.redirect("/")
            else:
                self.authorize_redirect(
                    redirect_uri=redirect_uri,
                    client_id=self.get_google_oauth_settings()['key'],
                    scope=['profile', 'email'],
                    response_type='code',
                    extra_params={'approval_prompt': 'auto'})

在版本 6.0 中更改: 删除了 callback 参数。使用返回的可等待对象代替。

Facebook

class tornado.auth.FacebookGraphMixin[source]

使用新的 Graph API 和 OAuth2 进行 Facebook 身份验证。

coroutine get_authenticated_user(redirect_uri: str, client_id: str, client_secret: str, code: str, extra_fields: Optional[Dict[str, Any]] = None) Optional[Dict[str, Any]][source]

处理 Facebook 用户的登录,返回一个用户对象。

示例用法

class FacebookGraphLoginHandler(tornado.web.RequestHandler,
                                tornado.auth.FacebookGraphMixin):
  async def get(self):
    redirect_uri = urllib.parse.urljoin(
        self.application.settings['redirect_base_uri'],
        self.reverse_url('facebook_oauth'))
    if self.get_argument("code", False):
        user = await self.get_authenticated_user(
            redirect_uri=redirect_uri,
            client_id=self.settings["facebook_api_key"],
            client_secret=self.settings["facebook_secret"],
            code=self.get_argument("code"))
        # Save the user with e.g. set_signed_cookie
    else:
        self.authorize_redirect(
            redirect_uri=redirect_uri,
            client_id=self.settings["facebook_api_key"],
            extra_params={"scope": "user_posts"})

此方法返回一个字典,其中可能包含以下字段

  • access_token,一个字符串,可以传递给 facebook_request

  • session_expires,一个以字符串形式编码的整数,表示访问令牌在秒内过期的时间。此字段应类似于 int(user['session_expires']) 使用;在 Tornado 的未来版本中,它将从字符串更改为整数。

  • id, name, first_name, last_name, locale, picture, link,以及 extra_fields 参数中命名的任何字段。这些字段从 Facebook Graph API user object 复制而来

Changed in version 4.5: The session_expires field was updated to support changes made to the Facebook API in March 2017.

在版本 6.0 中更改: 删除了 callback 参数。使用返回的可等待对象代替。

coroutine facebook_request(path: str, access_token: Optional[str] = None, post_args: Optional[Dict[str, Any]] = None, **args: Any) Any[source]

获取给定的相对 API 路径,例如,“/btaylor/picture”。

如果请求是 POST,则应提供 post_args。查询字符串参数应作为关键字参数给出。

有关 Facebook Graph API 的介绍,请访问 http://developers.facebook.com/docs/api

许多方法需要 OAuth 访问令牌,您可以通过 authorize_redirectget_authenticated_user 获取。通过该过程返回的用户包含一个 access_token 属性,可用于通过此方法进行身份验证请求。

示例用法

class MainHandler(tornado.web.RequestHandler,
                  tornado.auth.FacebookGraphMixin):
    @tornado.web.authenticated
    async def get(self):
        new_entry = await self.facebook_request(
            "/me/feed",
            post_args={"message": "I am posting from my Tornado application!"},
            access_token=self.current_user["access_token"])

        if not new_entry:
            # Call failed; perhaps missing permission?
            self.authorize_redirect()
            return
        self.finish("Posted a message!")

给定的路径相对于 self._FACEBOOK_BASE_URL,默认值为“https://graph.facebook.com”。

此方法是 OAuth2Mixin.oauth2_request 的包装器;唯一的区别是此方法采用相对路径,而 oauth2_request 采用完整的 URL。

Changed in version 3.1: Added the ability to override self._FACEBOOK_BASE_URL.

在版本 6.0 中更改: 删除了 callback 参数。使用返回的可等待对象代替。

Twitter

class tornado.auth.TwitterMixin[source]

Twitter OAuth 身份验证。

要使用 Twitter 进行身份验证,请在 http://twitter.com/apps 上注册您的应用程序。然后将您的 Consumer Key 和 Consumer Secret 复制到应用程序 settings twitter_consumer_keytwitter_consumer_secret。在您将注册为应用程序回调 URL 的 URL 处理程序上使用此 mixin。

设置好应用程序后,您可以像这样使用此 mixin 来使用 Twitter 认证用户并获取其流的访问权限

class TwitterLoginHandler(tornado.web.RequestHandler,
                          tornado.auth.TwitterMixin):
    async def get(self):
        if self.get_argument("oauth_token", None):
            user = await self.get_authenticated_user()
            # Save the user using e.g. set_signed_cookie()
        else:
            await self.authorize_redirect()

get_authenticated_user 返回的用户对象包括属性 username, name, access_token,以及在 https://dev.twitter.com/docs/api/1.1/get/users/show 中描述的所有自定义 Twitter 用户属性

Deprecated since version 6.3: This class refers to version 1.1 of the Twitter API, which has been deprecated by Twitter. Since Twitter has begun to limit access to its API, this class will no longer be updated and will be removed in the future.

协程 authenticate_redirect(callback_uri: Optional[str] = None) None[source]

类似于 authorize_redirect,但在授权后会自动重定向。

如果您使用 Twitter 进行单点登录,这通常是正确的接口。

在 3.1 版本中更改: 现在返回一个 Future 并接受一个可选的回调,以与 gen.coroutine 兼容。

在版本 6.0 中更改: 删除了 callback 参数。使用返回的可等待对象代替。

协程 twitter_request(path: str, access_token: Dict[str, Any], post_args: Optional[Dict[str, Any]] = None, **args: Any) Any[source]

获取给定的 API 路径,例如 statuses/user_timeline/btaylor

路径不应包含格式或 API 版本号。(我们自动使用 JSON 格式和 API 版本 1)。

如果请求是 POST,则应提供 post_args。查询字符串参数应作为关键字参数给出。

所有 Twitter 方法都记录在 http://dev.twitter.com/

许多方法需要 OAuth 访问令牌,您可以通过 authorize_redirectget_authenticated_user 获取。通过该过程返回的用户包含一个 ‘access_token’ 属性,可用于通过此方法进行身份验证请求。示例用法

class MainHandler(tornado.web.RequestHandler,
                  tornado.auth.TwitterMixin):
    @tornado.web.authenticated
    async def get(self):
        new_entry = await self.twitter_request(
            "/statuses/update",
            post_args={"status": "Testing Tornado Web Server"},
            access_token=self.current_user["access_token"])
        if not new_entry:
            # Call failed; perhaps missing permission?
            await self.authorize_redirect()
            return
        self.finish("Posted a message!")

在版本 6.0 中更改: 删除了 callback 参数。使用返回的可等待对象代替。