package com.crm.auth.security; import com.crm.base.security.DataVisibility; import com.crm.base.security.DataVisibilityContext; import org.springframework.security.core.authority.SimpleGrantedAuthority; import java.util.ArrayList; import java.util.List; import java.util.Map; import java.util.Set; /** * 权限解析结果:一次 resolve 拿齐一个用户的三份授权事实(ADR-0011)。 * *

不可变。纯取值对象——装载 {@link DataVisibilityContext} 是请求生命周期所有者 * (JWT 过滤器)的职责,本对象不产生副作用。

* * */ public final class PermissionGrant { /** Spring Security 角色 authority 前缀(hasRole('X') 实际匹配 ROLE_X) */ private static final String ROLE_PREFIX = "ROLE_"; private final DataVisibility visibility; private final Set permCodes; private final Map denyBehaviors; private final Set roleCodes; public PermissionGrant(DataVisibility visibility, Set permCodes, Set roleCodes) { this(visibility, permCodes, Map.of(), roleCodes); } public PermissionGrant(DataVisibility visibility, Set permCodes, Map denyBehaviors, Set roleCodes) { this.visibility = visibility; this.permCodes = permCodes == null ? Set.of() : Set.copyOf(permCodes); this.denyBehaviors = denyBehaviors == null ? Map.of() : Map.copyOf(denyBehaviors); this.roleCodes = roleCodes == null ? Set.of() : Set.copyOf(roleCodes); } public DataVisibility getVisibility() { return visibility; } /** 权限码并集:button 节点 perms 的去重集合,已排除停用权限点 */ public Set getPermCodes() { return permCodes; } /** * 权限码 → 无权限时前端表现(hide 隐藏 / disable 禁用),与 {@link #getPermCodes()} * 同源同断路规则;前端按钮显隐据此渲染。未配置的权限点按 hide 兜底。 */ public Map getDenyBehaviors() { return denyBehaviors; } /** 角色编码集合(原样,不带 ROLE_ 前缀约定) */ public Set getRoleCodes() { return roleCodes; } /** * 合成 Spring Security authorities:权限码原样注入;角色编码幂等归一到 ROLE_ 前缀 * (已带前缀的不重复加),使 hasAuthority / hasRole 各自按约定匹配。 * 前缀约定由本模块保证,调用方与 DB 数据无需理解它。 */ public List asAuthorities() { List authorities = new ArrayList<>(permCodes.size() + roleCodes.size()); permCodes.forEach(perm -> authorities.add(new SimpleGrantedAuthority(perm))); roleCodes.forEach(role -> authorities.add(new SimpleGrantedAuthority(normalizeRole(role)))); return List.copyOf(authorities); } private static String normalizeRole(String roleCode) { return roleCode.startsWith(ROLE_PREFIX) ? roleCode : ROLE_PREFIX + roleCode; } }