如何在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),安全性更高。
后端存储
这里分两种情况:
- 无状态JWT(不需要存储):如果用JWT的话,Token本身已经包含了用户信息和过期时间,后端不需要存储,只需要用密钥解密验证即可。这种方式轻量,适合大部分场景,但缺点是无法主动让Token失效(除非等它过期)。
- 需要主动失效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
相关产品推荐
相关产品推荐

