不建议序列化权限树对象持久化,因其存在安全风险(如反序列化漏洞)、可维护性差、无法SQL查询与校验、分布式不友好;应采用结构化存储+运行时构建树,并用JSON或Protobuf替代Java原生序列化。

在 Java 授权中心保存用户的权限树对象时,序列化本身不是首选方案,而是应结合安全、可维护和分布式场景综合设计。权限树(如菜单树、资源树、RBAC 权限结构)通常是结构化、有层级、需频繁查询/校验的数据,直接序列化为字节存数据库或缓存,会带来可读性差、版本兼容难、无法索引、SQL 查询失效、反序列化风险高等问题。
为什么一般不建议序列化权限树对象持久化
安全性风险:Java 原生序列化(Serializable)易受反序列化漏洞影响(如 Apache Commons Collections 反序列化链),授权中心属高危模块,禁用原生序列化是主流安全规范。
可维护性差:权限树结构常随业务演进(新增字段、调整父子关系、增加状态标记),序列化后难以做字段级变更或数据迁移。
查询与校验困难:无法用 SQL 查询“所有拥有‘删除订单’权限的用户”,也无法在数据库层做权限有效性校验(如过期、禁用)。
分布式不友好:微服务中多个服务需读取权限树,序列化 blob 不利于跨语言(如网关用 Go)、缓存穿透处理、增量更新。
推荐做法:结构化存储 + 按需构建树
将权限树拆解为规范的关系模型或文档模型,运行时按需组装:
-
数据库表设计示例:
•sys_permission(ID、code、name、type[menu/api/button]、path、parent_id、sort、status)
•sys_role_permission(role_id, permission_id)
•sys_user_role(user_id, role_id)
查询时通过 JOIN 或多次查询 + 内存组装(如用 Map 构建父子映射)生成树形结构。 -
缓存优化:
• 将用户权限 ID 列表(List<string></string>)存 Redis(如user:1001:perms→["menu:order", "api:order:delete"])
• 权限元数据(菜单树)单独缓存为 JSON 字符串(perm:tree:v2),使用 Jackson 序列化,非 Java 原生序列化,安全且跨语言。 -
运行时构建树:
• 加载用户权限 code 列表后,查库或从缓存获取完整权限节点集合;
• 用Map<string permissionnode></string>存所有节点,遍历一次建立父子引用,时间复杂度 O(n);
• 返回标准 DTO(如PermissionTreeDTO),不含任何Serializable逻辑。
如果必须序列化(极少数离线/临时场景)
仅限可信内部环境、短期缓存、无外部输入的场景,且必须规避原生序列化:
- 用 Jackson 或 FastJSON2(禁用 autoType)转为 JSON 字符串,存入数据库 TEXT 字段或 Redis:
ObjectMapper.writeValueAsString(permissionTree) - 避免使用
java.io.Serializable+ObjectOutputStream;
禁用 FastJSON 的ParserConfig.getGlobalInstance().setAutoTypeSupport(false)。 - 若需二进制紧凑存储(如嵌入式),选用 Protobuf 或 Avro,定义明确 schema,生成强类型类,天然防反序列化攻击。
补充:Spring Security 集成建议
授权中心通常对接 Spring Security,注意:
立即学习“Java免费学习笔记(深入)”;
- 不要把整个权限树塞进
Authentication.getPrincipal()或SecurityContext;
应只存关键标识(如 userId、roles),权限校验走Authority(如SimpleGrantedAuthority("PERM_API_ORDER_DELETE"))。 - 自定义
FilterSecurityInterceptor或@PreAuthorize表达式时,权限判断逻辑从缓存/DB 动态加载,而非依赖反序列化对象。 - 前端所需菜单树,由独立接口提供(如
GET /api/v1/me/menu),后端组装并返回 JSON,不复用认证上下文中的序列化数据。


















