You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何在Tornado中实现API调用的Token认证及Token存储

嘿,我来帮你捋清楚Tornado里基于Token的API认证怎么搞,还有Token该存哪儿的问题~

一、实现基于Token的API认证(参照你提到的思路)

在Tornado里,我们可以通过**自定义装饰器+JWT(JSON Web Token)**的方式来实现每个API调用的Token认证,步骤如下:

1. 先准备依赖

首先得装PyJWT库来生成和解析Token:

pip install pyjwt python-jose[cryptography]

2. 写Token认证装饰器

这个装饰器会帮我们在每个需要认证的接口前验证Token的有效性:

from tornado.web import HTTPError
import jwt
from datetime import datetime

def token_required(func):
    def wrapper(self, *args, **kwargs):
        # 从请求头的Authorization字段获取Token(格式:Bearer <token>)
        auth_header = self.request.headers.get("Authorization")
        if not auth_header or not auth_header.startswith("Bearer "):
            raise HTTPError(401, "请提供有效的认证Token")
        
        token = auth_header.split(" ")[1]
        try:
            # 解密Token,拿到用户信息
            payload = jwt.decode(
                token, 
                "your-strong-secret-key",  # 这里换成你的保密密钥,别硬编码!
                algorithms=["HS256"],
                options={"verify_exp": True}
            )
            # 把用户ID绑定到当前handler,后续接口可以直接用
            self.current_user_id = payload["user_id"]
        except jwt.ExpiredSignatureError:
            raise HTTPError(401, "Token已过期,请重新登录")
        except jwt.InvalidTokenError:
            raise HTTPError(401, "无效的认证Token")
        
        return func(self, *args, **kwargs)
    return wrapper

3. 写登录接口(生成Token)

用户登录验证通过后,生成Token返回给前端:

class LoginHandler(tornado.web.RequestHandler):
    def post(self):
        # 获取前端传的用户名密码
        username = self.get_body_argument("username")
        password = self.get_body_argument("password")
        
        # 这里替换成你的实际用户验证逻辑(比如查数据库)
        if username == "test" and password == "123456":
            user_id = 1001  # 从数据库拿到的真实用户ID
            # 生成带过期时间的Token(比如24小时有效期)
            token = jwt.encode(
                {
                    "user_id": user_id,
                    "exp": datetime.utcnow() + datetime.timedelta(hours=24)
                },
                "your-strong-secret-key",
                algorithm="HS256"
            )
            self.write({"code": 200, "access_token": token, "message": "登录成功"})
        else:
            self.write({"code": 400, "message": "用户名或密码错误"})

4. 保护需要认证的API接口

给需要权限的接口加上我们写的装饰器就行:

class UserProfileHandler(tornado.web.RequestHandler):
    @token_required
    def get(self):
        # 这里可以用self.current_user_id获取当前登录用户ID,查数据库返回信息
        self.write({
            "code": 200,
            "data": {"user_id": self.current_user_id, "username": "test", "email": "test@example.com"}
        })
二、Access Token的存储方案(Tornado无Session的情况下)

因为Tornado本身不提供Session存储,所以得分前端和后端两个维度来考虑:

前端存储

  • 推荐方案:把Token存在localStorage(持久化,关闭浏览器也保留)或者sessionStorage(仅当前会话有效,关闭浏览器就清除),前端每次请求时,把Token放到Authorization: Bearer <token>的请求头里发送给后端。
  • 备选方案:存在HttpOnly+Secure的Cookie里,这样能防止XSS攻击,同时配合CSRF防护(比如Tornado的xsrf_cookie),安全性更高。

后端存储

这里分两种情况:

  1. 无状态JWT(不需要存储):如果用JWT的话,Token本身已经包含了用户信息和过期时间,后端不需要存储,只需要用密钥解密验证即可。这种方式轻量,适合大部分场景,但缺点是无法主动让Token失效(除非等它过期)。
  2. 需要主动失效Token的场景:如果要支持用户注销、修改密码后立即失效Token,就得在后端存储Token的状态:
    • Redis存储:把Token作为key,过期时间和JWT一致,注销时删除对应的key;或者维护一个Token黑名单,把失效的Token加进去,验证时先查黑名单。Redis的性能很高,适合高频请求的场景。
    • 数据库存储:建一个user_tokens表,记录user_id、token、expire_time,每次验证Token时先查这个表是否存在且未过期。但数据库的读写性能不如Redis,适合并发量不高的场景。

最后提醒一下:密钥一定要保密,别硬编码在代码里,最好用环境变量来配置;Token的过期时间别设太长,可以配合刷新Token机制,让用户不用频繁登录。

内容的提问来源于stack exchange,提问作者Shyju

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.05.28 09:44:14