在Spring Authorization Server中为AccessToken添加租户信息
问题解答
1. 在JwtCustomizer中获取当前认证用户并添加租户角色Claims
你可以通过JwtEncodingContext.getPrincipal()拿到当前的Authentication对象,从中提取用户标识(比如用户ID),再查询数据库获取该用户关联的所有租户及对应角色,最后把这些信息写入JWT的Claims中。
具体实现步骤:
- 注入你的用户/租户数据访问服务(比如
UserTenantService) - 在
jwtCustomizer中提取认证用户的核心信息 - 查询用户对应的租户角色数据并构造指定格式
- 将数据添加到JWT Claims中
示例代码:
@Bean public OAuth2TokenCustomizer<JwtEncodingContext> jwtCustomizer(UserTenantService userTenantService) { return context -> { if (context.getTokenType().equals(OAuth2TokenType.ACCESS_TOKEN)) { // 获取当前认证用户的Authentication对象 Authentication authentication = context.getPrincipal(); // 假设你的用户信息封装在自定义的UserDetails实现里 CustomUserDetails user = (CustomUserDetails) authentication.getPrincipal(); // 查询该用户关联的所有租户与角色 List<TenantRoleDto> tenantRoles = userTenantService.getTenantRolesByUserId(user.getId()); // 将租户角色数据写入JWT Claims context.getClaims().claim("tenants", tenantRoles); } }; }
注意:要确保你的CustomUserDetails包含用户ID等查询所需字段,UserTenantService能正确从数据库读取用户-租户-角色的关联关系。
2. 给/token接口传入tenantId,返回仅该租户的AccessToken
这种方案完全可行,核心是在Token请求阶段接收tenantId参数,然后只将该租户的角色信息写入JWT。需要做以下改造:
步骤1:扩展Token请求参数,接收tenantId
自定义OAuth2TokenRequestConverter,把请求中的tenantId参数提取出来,存入Authentication的details中:
@Component public class CustomTokenRequestConverter extends OAuth2AuthorizationCodeGrantRequestConverter { @Override public OAuth2AuthorizationCodeGrantRequest convert(HttpServletRequest request) { OAuth2AuthorizationCodeGrantRequest grantRequest = super.convert(request); String tenantId = request.getParameter("tenantId"); if (tenantId != null) { // 将tenantId存入Authentication的details字段 Authentication authentication = grantRequest.getAuthentication(); Map<String, Object> details = new HashMap<>(); details.putAll(authentication.getDetails() != null ? (Map) authentication.getDetails() : Collections.emptyMap()); details.put("tenantId", tenantId); // 构造携带tenantId的新Authentication对象 Authentication newAuth = new UsernamePasswordAuthenticationToken( authentication.getPrincipal(), authentication.getCredentials(), authentication.getAuthorities() ); newAuth.setDetails(details); // 返回更新后的授权请求 return new OAuth2AuthorizationCodeGrantRequest( grantRequest.getClientRegistration(), grantRequest.getAuthorizationExchange(), newAuth ); } return grantRequest; } }
步骤2:在JwtCustomizer中获取tenantId并筛选角色
修改之前的jwtCustomizer,从Authentication的details中取出tenantId,仅查询该租户的角色信息:
@Bean public OAuth2TokenCustomizer<JwtEncodingContext> jwtCustomizer(UserTenantService userTenantService) { return context -> { if (context.getTokenType().equals(OAuth2TokenType.ACCESS_TOKEN)) { Authentication authentication = context.getPrincipal(); CustomUserDetails user = (CustomUserDetails) authentication.getPrincipal(); Map<String, Object> details = (Map<String, Object>) authentication.getDetails(); List<TenantRoleDto> tenantRoles; if (details.containsKey("tenantId")) { String tenantId = (String) details.get("tenantId"); // 仅查询指定租户的角色信息 tenantRoles = Collections.singletonList(userTenantService.getTenantRoleByUserIdAndTenantId(user.getId(), tenantId)); } else { // 未传tenantId时返回所有租户角色 tenantRoles = userTenantService.getTenantRolesByUserId(user.getId()); } context.getClaims().claim("tenants", tenantRoles); } }; }
两种方案对比
- 传tenantId到/token接口:用户每次只能绑定一个租户,每个租户对应独立AccessToken,后续请求无需携带租户标识,适合用户固定在单个租户下操作的场景。
- 请求头携带tenantId:单个AccessToken支持跨租户操作,灵活性更高,但每次请求都需携带租户标识,适合用户频繁切换租户的场景。
内容的提问来源于stack exchange,提问作者a4dev92
相关产品推荐
相关产品推荐

