You need to enable JavaScript to run this app.
最新活动
大模型
产品
解决方案
定价
生态与合作
支持与服务
开发者
了解我们

基于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_idtoken_jtiexpires_at,登录时查询该用户是否有expires_at > 当前时间的记录,退出时删除对应记录。
  • 多设备登录场景:如果需要支持用户在多设备登录,但同一设备不能重复登录,可以在存储时加上设备标识(比如前端传设备ID),检查时针对设备ID判断。
  • 令牌过期自动清理:Redis的expire会自动处理过期键,数据库的话可以定期跑脚本删除过期记录,避免数据冗余。

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

火山引擎 最新活动