通用黑白名单方案
背景
设计一套灵活的黑白名单机制,要求无需修改代码即可修改黑白名单逻辑的切换。使用场景如下,比如原先一个新功能上线,我们期望给部分企业使用;等功能成熟后,我们一般会开放给所有的企业一起使用,但是好巧不巧,一家企业不愿意使用这个功能。我们不可能给每一个企业配置白名单。这样,我们就需要一个黑名单功能,即实现单独给不愿意使用新功能的企业单独配置一个名单。
为了不修改代码,我们只需要修改名单类型,配置相关名单的企业就好了。
方案设计
+----------------+ +-----------------+
| Feature Flag | | Configuration |
| Management | | Storage |
| System | | (e.g., JSON, |
+-------+--------+ | Database, etc.) || +--------+---------+| || || +------------+ |+----->+ Blacklist +<-----++------------+^|v+------------+| Whitelist |+------------+^|v+------------+| Enterprise || Service |
需要新建两张表:
- 开关表设计:表明自己是白名单、还是黑名单,以及对应的开关状态。
- 控制关联表则记录关联的企业ID。
方案可行性评估:
提出的黑白名单机制在设计上是可行的,可以通过数据库表来控制名单的逻辑和状态。通过这种设计,可以在不修改代码的情况下,通过更新数据库表来切换黑白名单的逻辑和修改名单内容。这种方法可以提供灵活的配置管理,并且可以即时生效,符合要求。
数据库表设计:
我们需要两张表,一张是名单模式开关表(list_switch
),另一张是名单关联表(list_association
)。
- 名单模式开关表(
list_switch
):用来记录当前名单模式是黑名单还是白名单,以及开关状态。
CREATE TABLE `list_switch` (`id` INT AUTO_INCREMENT PRIMARY KEY,`list_type` ENUM('BLACKLIST', 'WHITELIST') NOT NULL,`is_active` BOOLEAN NOT NULL DEFAULT TRUE,`updated_at` TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP
);
- 名单关联表(
list_association
):记录关联的企业ID,与名单模式开关表关联。
CREATE TABLE `list_association` (`id` INT AUTO_INCREMENT PRIMARY KEY,`enterprise_id` INT NOT NULL,`list_switch_id` INT NOT NULL,FOREIGN KEY (`list_switch_id`) REFERENCES `list_switch`(`id`)
);
示例代码:
以下是一个简单的 Spring Boot 服务层示例,演示了如何根据数据库表中的配置来决定是否允许某个企业ID执行操作。
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.jdbc.core.JdbcTemplate;@Service
public class ListService {@Autowiredprivate JdbcTemplate jdbcTemplate;public boolean isAllowed(int enterpriseId) {// 查询当前名单模式和状态String listSwitchSql = "SELECT list_type, is_active FROM list_switch WHERE is_active = TRUE LIMIT 1";Map<String, Object> listSwitch = jdbcTemplate.queryForMap(listSwitchSql);String listType = (String) listSwitch.get("list_type");boolean isActive = (Boolean) listSwitch.get("is_active");if (!isActive) {// 如果名单模式未激活,允许所有操作return true;}// 查询企业ID是否在名单关联表中String listAssociationSql = "SELECT COUNT(1) FROM list_association WHERE enterprise_id = ?";int count = jdbcTemplate.queryForObject(listAssociationSql, new Object[]{enterpriseId}, Integer.class);if ("WHITELIST".equals(listType)) {// 如果是白名单模式,只有在名单中的企业ID才允许操作return count > 0;} else if ("BLACKLIST".equals(listType)) {// 如果是黑名单模式,名单中的企业ID不允许操作return count == 0;}// 默认允许操作return true;}
}
在这个示例中,isAllowed
方法首先检查名单模式是否激活,并获取当前的名单类型。然后,它查询关联表以确定企业ID是否在名单中。根据名单类型(黑名单或白名单),方法返回是否允许企业ID执行操作。
这个示例提供了一个基础的实现,但在实际应用中,你可能需要添加更多的逻辑来处理并发更新、缓存名单状态以提高性能、提供管理接口来更新名单等。此外,为了保证数据的一致性和可靠性,你可能还需要在数据库操作中使用事务管理。