跳转到正文

权限与状态扩展

Authentication 提供权限模型和扩展契约,应用负责把角色来源、操作检查和身份状态变化连接起来。不要仅注册 Hook 就假定权限拦截或退出清理已经生效。

从角色数据到操作检查

RoleHook 提供 listRoleName(loginUser)listOperationByModule(loginUser, module),分别返回角色名与模块操作集合。UserDetailHook 提供 detailKey()detail(loginUser),用于描述额外身份详情。它们继承 Constant 序列 Hook 契约,但 Authentication 不收集、排序或执行这两类 Hook。

接入方式可以是:

系统菜单业务已经提供 SystemMenuRoleHook。菜单 code、操作字符串及客户端过滤遵循该业务实现,不由 Authentication 统一规定。仅添加系统菜单 Hook 和 Authentication,并不会自动装配 Business 登录模块的聚合器。

checkOperation(module, operations) 只返回布尔值,不自己拒绝 HTTP 请求。应用必须使用返回值或启用合适的 Security 拦截;通用回调没有内置多操作“全部满足”规则,示例和 Business 默认实现各自明确了这一策略。

装配权限时间戳服务

PermissionCacheService 保存“权限数据已变化”的时间戳。它不保存完整角色或操作集合,不扫描删除旧缓存,也不替应用重新加载权限。

应用已有 StoreClient 时,自动配置提供名为 permissionCacheStoreTemplate 的字符串模板及 PermissionCacheService。最小本地后端依赖为:

xml
<dependency>
    <groupId>com.own.component</groupId>
    <artifactId>springboot-component-store-starter-local</artifactId>
</dependency>

版本沿用 Component BOM。多实例共享失效信号时使用共享 Store 后端,本地后端只能影响同一客户端内的数据。

内容默认行为
创建条件存在 StoreClient;没有 Store 时不创建模板和服务
命名空间显式使用 authentication-permission,不是默认模板的 own.store.namespace
CodecStoreCodecs.string()
模板覆盖按 Bean 名 permissionCacheStoreTemplate 缺失条件创建
服务覆盖PermissionCacheService 类型缺失条件创建

多个系统共用 Redis 时,需要同时考虑权限专用命名空间和默认业务缓存命名空间。仅调整 own.store.namespace 不会改变这里的固定命名空间;需要隔离时,在存在 StoreClient 的应用中提供同名模板,例如:

java
package com.example.config;

import com.own.component.store.api.StoreClient;
import com.own.component.store.api.StoreTemplate;
import com.own.component.store.api.common.StoreCodecs;
import com.own.component.store.api.common.StoreNamespace;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;

@Configuration(proxyBeanMethods = false)
public class PermissionStoreConfiguration {
    @Bean("permissionCacheStoreTemplate")
    StoreTemplate<String> permissionCacheStoreTemplate(StoreClient client) {
        return client.template(new StoreNamespace("example-permission"), StoreCodecs.string());
    }
}

同一应用的各实例使用一致的自定义空间;需要隔离的其他应用使用不同空间。只提供同名模板但没有 StoreClient,不会满足该自动配置的整体条件,此时应同时显式注册服务。

更新失效时间戳

方法行为
setGlobalTimestamp()在逻辑键 global-timestamp 写入当前毫秒时间的十进制字符串
getGlobalTimestamp()读取该值,不存在返回 null
setUserRoleTimestamp(userId)user-role-timestamp:<userId> 写入当前毫秒时间字符串
getUserRoleTimestamp(userId)读取该用户值,不存在返回 null

用户 ID 必须非 null 且大于 0,非法值抛出 IllegalArgumentException。写入不指定 TTL,默认使用 Clock.systemUTC();这些值不是有序事件 ID,同一毫秒的两次更新可能相同,时钟回拨或并发跨实例写入也不保证单调递增。

应用在权限写入成功后调用相应 setter,并在读取缓存时将时间戳纳入键或版本判断,才能产生失效效果。时间戳更新不参加数据库事务,不自动延迟到提交后;需要提交后一致性时由应用编排。Store 写入异常会直接传播,没有重试或降级。

未设置 TTL 不代表永久可靠:本地容量淘汰、进程重启和后端清理都可能让值变回 null,具体见Store 容量和命名空间。读取方需要设计 null 与旧键复用时的处理,不能只依靠毫秒值承诺每次变更即时刷新。

与当前 Business 登录缓存衔接

当前 Business RoleHookCore 的模块操作缓存键包含全局时间戳、用户角色时间戳、客户端和用户 ID;角色名称缓存键只包含全局时间戳和用户 ID,没有用户角色时间戳或客户端。

因此只更新用户角色时间戳可以影响该用户的操作权限缓存键,不能保证角色名称缓存同步刷新;不同客户端的角色名也可能共享键。应用依赖实时角色名时应补充清理或调整缓存键。该差异属于实际消费者实现,Authentication 的 setter 不会替它自动补齐。

系统菜单各写入入口的刷新时点见角色与授权。Authentication 的 request 缓存仍与上述 Store 机制独立,更新 Store 时间戳不会替换当前请求已取得的权限对象。

状态清理与登录 Hook

UserStatusSpringHook 收集 Spring 提供的 UserStatusHook 列表。应用显式调用 clear(loginUser) 时,按注入列表顺序同步执行各 Hook 的 clear;需要排序可通过 Spring 的集合注入排序机制声明。没有额外的异常隔离,某项抛异常会中断后续调用,也不提供回滚。

该调度器不自动挂到退出接口、Token 过期或用户禁用事件。只有实际 Hook 才能决定清理什么;空列表时调用没有清理效果。它也不会内置清除 Authentication 的两个 request 缓存属性。应用应在可信的状态变更流程中传入正确身份并显式调用,避免把“存在这个 Bean”当作已完成退出。

Hook 契约内容与默认边界
UserStatusHook.clear(user)用户状态清理动作;由上述调度器显式执行
UserDetailHook用户详情键和值;Authentication 不执行,Business 登录流程可消费
LoginSecurityHook.check(client, userId, userType, request)返回 flag/code/message;client() 默认 null,客户端选择与失败策略由调用方决定
LoginOperationHook.before() / after()登录前后处理契约;Authentication 没有自动调用链

LoginSecurityHookEntity.SUCCESS 表示 true / 00000 / 验证成功。该结果只是数据模型,不会自动抛异常、拦截请求或调用登录接口;自定义登录流程必须检查结果并决定后续行为。

示例涉及的源码类型

StoreTemplateStoreNamespace