基于Flask-RESTful与Flask-JWT-Extended,登录前如何检查用户是否已登录?
如何用Flask-JWT-Extended检查用户是否已登录
嘿,这个需求我之前也碰到过,用Flask-JWT-Extended实现的核心思路是追踪用户的活跃令牌——毕竟JWT本身是无状态的,服务器默认不会记录谁正处于登录状态,所以我们得自己加个存储层来搞定。下面给你一步步拆解具体实现:
1. 选择令牌存储方式
最常用的是Redis(轻量、支持过期时间,适合存临时的令牌数据),当然也可以用数据库表。这里以Redis为例,先安装依赖:
pip install redis flask-redis
2. 初始化Redis与核心配置
先把Flask、JWT和Redis的基础配置搭好:
from flask import Flask, request from flask_restful import Api, Resource from flask_jwt_extended import ( JWTManager, create_access_token, jwt_required, get_jwt_identity, get_jwt ) import redis app = Flask(__name__) # 替换成你的JWT密钥 app.config['JWT_SECRET_KEY'] = 'your-strong-secret-key' # 可选:设置JWT过期时间,比如1小时 app.config['JWT_ACCESS_TOKEN_EXPIRES'] = 3600 jwt = JWTManager(app) api = Api(app) # 初始化Redis连接(根据你的Redis配置调整) redis_client = redis.Redis( host='localhost', port=6379, db=0, decode_responses=True # 自动解码为字符串,避免字节操作 )
3. 修改登录接口:检查活跃令牌
在登录时,先查询存储中是否存在该用户的有效令牌,如果有就拒绝登录,否则生成令牌并存储:
class Login(Resource): def post(self): # 1. 获取并验证用户提交的登录信息(这里简化处理,实际要从request中取) login_data = request.get_json() username = login_data.get('username') password = login_data.get('password') # 假设这里已经完成用户名密码验证,得到用户ID(实际从数据库查询) user_id = "123" # 替换为真实用户ID if not user_id: return {"msg": "用户名或密码错误"}, 401 # 2. 检查用户是否已有活跃令牌 user_token_key = f"active_tokens:{user_id}" # 获取该用户的所有有效令牌(Redis哈希结构存储) active_tokens = redis_client.hgetall(user_token_key) if active_tokens: # 可以额外检查令牌是否过期,不过Redis的expire会自动清理过期键,这里简化处理 return {"msg": "您已处于登录状态,请先退出登录"}, 403 # 3. 生成JWT令牌 access_token = create_access_token(identity=user_id) # 4. 将令牌存入Redis,设置过期时间与JWT一致 redis_client.hset(user_token_key, access_token, username) redis_client.expire(user_token_key, app.config['JWT_ACCESS_TOKEN_EXPIRES']) return {"access_token": access_token}, 200
4. 修改退出接口:清理活跃令牌
用户退出时,要从存储中移除对应的令牌,这样下次就能正常登录了:
class Logout(Resource): @jwt_required() def post(self): current_user_id = get_jwt_identity() # 获取当前令牌的唯一标识(jti),确保只移除当前使用的令牌 current_token_jti = get_jwt()['jti'] user_token_key = f"active_tokens:{current_user_id}" # 从Redis哈希中删除当前令牌 redis_client.hdel(user_token_key, current_token_jti) # 如果该用户没有剩余活跃令牌,删除整个键节省空间 if not redis_client.hgetall(user_token_key): redis_client.delete(user_token_key) return {"msg": "退出登录成功"}, 200
额外注意事项
- 如果用数据库代替Redis:可以创建一张
active_tokens表,字段包括user_id、token_jti、expires_at,登录时查询该用户是否有expires_at > 当前时间的记录,退出时删除对应记录。 - 多设备登录场景:如果需要支持用户在多设备登录,但同一设备不能重复登录,可以在存储时加上设备标识(比如前端传设备ID),检查时针对设备ID判断。
- 令牌过期自动清理:Redis的
expire会自动处理过期键,数据库的话可以定期跑脚本删除过期记录,避免数据冗余。
内容的提问来源于stack exchange,提问作者Meshack Mbuvi




