本文档说明 fs-admin、fs-company 两模块在已有部分改造基础上的补充与完善点,以及本次已完成的改动。
SysLoginService.login(..., tenantCode),支持传 tenantCode 查租户、切库、LoginUser.setTenantId。tenantId 切数据源、设置 TenantConfigContext、ProjectConfig.loadTenantConfigsFromContext()。tenantCode 登录、主库查租户、TenantDataSourceManager.switchTenant、登录成功后 loginUser.setTenantId。tenantId 切库、设置 TenantConfigContext、ProjectConfig.loadTenantConfigsFromContext()。LoginUser 是 framework.security.LoginUser,不是 common 的 LoginUser。Redis 的 TenantKeyRedisSerializer 依赖 SecurityUtils.getTenantId() 给 key 加租户前缀,若只判断 principal instanceof LoginUser(common),company 登录后 principal 不匹配,导致 Redis key 无租户前缀。SecurityUtils.getTenantId() 中,除 LoginUser 外,对任意 principal 用反射调用 getTenantId(),若返回 Long 则使用,从而兼容 framework 的 LoginUser。fs-common/.../SecurityUtils.java。fs-admin/.../tenant/TenantInfoController.java。targetDataSources 使用 DataSourceType.MASTER(枚举)为 key,而 DynamicDataSourceContextHolder 使用 MASTER.name()(字符串)。为与 framework 及后续维护一致,建议统一为字符串 key。targetDataSources.put(DataSourceType.MASTER, ...) 改为 put(DataSourceType.MASTER.name(), ...)。fs-company/.../config/DataSourceConfig.java。company_login_tokens:uuid,多租户下不同租户相同 uuid 会互相覆盖;且 TenantKeyRedisSerializer 对 login_tokens 有特殊处理,而 company 使用 company_login_tokens,存/取时若未带租户信息,无法按租户隔离。loginUser.getTenantId() != null,将 tenantId 写入 JWT claims。getTokenKey(Long tenantId, String uuid):tenantId 非空时返回 tenantid:{tenantId}:company_login_tokens:{uuid}(与 TenantKeyRedisSerializer 约定一致,以 tenantid: 开头不再加前缀)。getTokenKey(loginUser.getTenantId(), loginUser.getToken()) 写入 Redis。getTokenKey(tenantId, uuid) 取 Redis。getTokenKey(tenantId, uuid) 删除。getTenantId() 的逻辑(如 Redis 序列化)在需要时能拿到租户。fs-company/.../service/TokenService.java、CompanyLoginService.java。| 模块 | 能力 | 说明 |
|---|---|---|
| fs-admin | 登录带 tenantCode | 与原有一致,framework 的 SysLoginService 处理。 |
| fs-admin | 请求按 tenantId 切库 + 租户配置 | 与原有一致,framework 的 JwtAuthenticationTokenFilter 处理。 |
| fs-admin | 租户管理接口 | 强制主库,避免租户身份下误查租户库。 |
| fs-company | 登录带 tenantCode | 与原有一致,CompanyLoginService 处理。 |
| fs-company | 请求按 tenantId 切库 + 租户配置 | 与原有一致,company 的 JwtAuthenticationTokenFilter 处理。 |
| fs-company | Redis Token 按租户隔离 | Token 存/取/删均带 tenantId,多租户下互不覆盖。 |
| fs-common | getTenantId() | 兼容 admin(common LoginUser)与 company(framework LoginUser)。 |
admin
company
Redis
| 文件 | 改动说明 |
|---|---|
fs-common/.../SecurityUtils.java |
getTenantId() 增加反射兼容 framework LoginUser |
fs-admin/.../tenant/TenantInfoController.java |
类上增加 @DataSource(DataSourceType.MASTER) |
fs-company/.../config/DataSourceConfig.java |
MASTER 使用 .name() 作为 map key |
fs-company/.../service/TokenService.java |
JWT 带 tenantId、getTokenKey(tenantId,uuid)、存/取/删按租户 key |
fs-company/.../service/CompanyLoginService.java |
生成 token 前设置 SecurityContext |
与整体 SaaS 方案的关系见 SaaS改造方案.md。