WEBKT

别再硬抗了!Redis + Lua 轻松搞定分布式令牌黑名单机制,拒绝恶意访问!

47 0 0 0

在互联网应用中,为了防止恶意访问,保障系统安全,我们经常需要实现一个黑名单机制。 而在分布式环境下,如何高效、可靠地实现黑名单机制就成了一个值得探讨的问题。 本文将结合 Redis 和 Lua 脚本,详细讲解如何设计并实现一个高效的分布式令牌黑名单机制。

1. 为什么选择 Redis + Lua?

  • Redis 的优势:

    • 高性能: Redis 是一个基于内存的 NoSQL 数据库,读写速度极快,非常适合处理高并发场景。
    • 原子性: Redis 的命令是原子性的,保证了在并发情况下的数据一致性,这对于黑名单机制至关重要。
    • 丰富的数据结构: Redis 提供了丰富的数据结构,如字符串、列表、集合等,方便我们存储和管理黑名单数据。
  • Lua 脚本的优势:

    • 原子性: Lua 脚本在 Redis 中执行是原子性的,可以保证一系列操作的完整性和一致性。
    • 灵活性: Lua 脚本允许我们在 Redis 中执行复杂的逻辑操作,例如判断令牌是否存在、添加令牌到黑名单等。
    • 减少网络开销: 使用 Lua 脚本可以将多个操作打包成一个脚本在服务器端执行,减少了客户端和服务器之间的网络交互。

2. 核心设计思路

我们的目标是设计一个基于令牌的黑名单机制,当一个令牌被判定为恶意时,将其加入黑名单,后续请求将被拒绝。设计思路如下:

  • 令牌生成: 用户登录或者其他需要身份验证的场景,生成一个唯一的令牌(例如 JWT)。
  • 令牌存储: 将令牌存储在 Redis 中,可以使用 SET 结构,并设置一个过期时间,避免令牌无限期地存在。
  • 黑名单存储: 使用 Redis 的 SET 结构来存储黑名单中的令牌。 当某个令牌被判定为非法时,将其添加到这个 SET 中。
  • 请求验证: 每次请求时,先验证令牌是否有效(是否存在于 Redis 中),再检查令牌是否在黑名单中。

3. Lua 脚本实现

我们使用 Lua 脚本来封装黑名单的检查和添加操作,保证原子性。

  • 检查令牌是否在黑名单中的 Lua 脚本 (is_token_in_blacklist.lua):

    -- KEYS[1]: 黑名单的 key
    -- ARGV[1]: 令牌
    local blacklist_key = KEYS[1]
    local token = ARGV[1]
    -- 判断令牌是否存在于黑名单中
    if redis.call('SISMEMBER', blacklist_key, token) == 1 then
    return 1 -- 在黑名单中
    else
    return 0 -- 不在黑名单中
    end
  • 将令牌添加到黑名单的 Lua 脚本 (add_token_to_blacklist.lua):

    -- KEYS[1]: 黑名单的 key
    -- ARGV[1]: 令牌
    local blacklist_key = KEYS[1]
    local token = ARGV[1]
    -- 将令牌添加到黑名单
    redis.call('SADD', blacklist_key, token)
    return 1

4. 代码示例 (Python)

import redis
import json
# Redis 连接配置
redis_host = 'localhost'
redis_port = 6379
redis_db = 0
# 连接 Redis
r = redis.Redis(host=redis_host, port=redis_port, db=redis_db)
# 加载 Lua 脚本
def load_lua_script(script_path):
with open(script_path, 'r') as f:
script_content = f.read()
return r.register_script(script_content)
# 加载黑名单检查脚本
is_token_in_blacklist = load_lua_script('is_token_in_blacklist.lua')
# 加载添加到黑名单的脚本
add_token_to_blacklist = load_lua_script('add_token_to_blacklist.lua')
# 检查令牌是否在黑名单中
def check_token_in_blacklist(token):
result = is_token_in_blacklist(keys=['blacklist'], args=[token])
return result
# 将令牌添加到黑名单
def add_token_to_blacklist_func(token):
add_token_to_blacklist(keys=['blacklist'], args=[token])
print(f'Token {token} added to blacklist.')
# 模拟请求验证
def validate_request(token):
if check_token_in_blacklist(token):
print('Token in blacklist. Request denied.')
return False
else:
print('Token valid. Request accepted.')
return True
# 示例用法
token1 = 'user1_token'
token2 = 'user2_token'
# 模拟用户请求
validate_request(token1) # Token valid. Request accepted.
validate_request(token2) # Token valid. Request accepted.
# 将 token1 加入黑名单
add_token_to_blacklist_func(token1)
# 再次验证
validate_request(token1) # Token in blacklist. Request denied.
validate_request(token2) # Token valid. Request accepted.
  • 代码说明:
    • 我们首先连接到 Redis 服务器。
    • 然后加载了两个 Lua 脚本: is_token_in_blacklist.luaadd_token_to_blacklist.lua
    • check_token_in_blacklist() 函数执行了黑名单检查脚本。
    • add_token_to_blacklist_func() 函数执行了添加令牌到黑名单的脚本。
    • validate_request() 函数模拟了请求验证的过程。

5. 进阶优化

  • 过期时间: 黑名单中的令牌可以设置一个过期时间,例如 1 天或者更短,避免永久封禁合法用户。 你可以使用 Redis 的 EXPIRE 命令来设置过期时间。

  • 令牌类型: 可以根据不同的业务场景,使用不同的令牌类型,例如用户令牌、设备令牌等。 针对不同的令牌类型,可以设置不同的黑名单策略。

  • 黑名单范围: 可以支持更细粒度的黑名单,比如 IP 黑名单、用户 ID 黑名单等。

  • 异步处理: 将添加令牌到黑名单的操作异步化,避免阻塞主线程。 例如,可以使用消息队列(如 Kafka)来实现。

  • 监控和报警: 监控黑名单的变化情况,如果黑名单中的令牌数量异常增加,及时报警,排查问题。

6. 总结

通过 Redis 和 Lua 脚本,我们构建了一个高效、可靠的分布式令牌黑名单机制。 这种方案兼顾了性能、原子性和灵活性,能够有效地保护系统安全。 当然,在实际应用中,还需要根据具体的业务场景进行调整和优化,例如设置合理的过期时间、采用更细粒度的黑名单、以及添加监控和报警机制等。

希望这篇文章对您有所帮助! 如果您在实际应用中遇到了问题,欢迎留言讨论! 让我们一起在技术的道路上不断前行! 嘿,对了,您还可以思考下,除了黑名单,还有什么其他方式来限制恶意访问呢? 欢迎留言,咱们一起聊聊!

后端架构师 RedisLua分布式令牌黑名单

评论点评

打赏赞助
sponsor

感谢您的支持让我们更好的前行

分享

QRcode

https://www.webkt.com/article/6994