package com.baiyue.common.security; import org.springframework.security.core.authority.SimpleGrantedAuthority; import org.springframework.security.oauth2.jwt.Jwt; import java.util.Collection; import java.util.LinkedHashSet; import java.util.List; import java.util.Set; /** * JWT 声明映射器,将令牌中的用户、租户、端类型和权限声明转换为认证主体与授权集合。 */ public class JwtClaimsMapper { public BaiyueJwtAuthenticationToken map(Jwt jwt) { return map(jwt, null); } public BaiyueJwtAuthenticationToken map(Jwt jwt, AccessTokenSession session) { Set permissions = new LinkedHashSet<>(); if (session != null) { permissions.addAll(session.permissions()); addRoles(permissions, session.roles()); } String appCode = firstNonBlank(jwt.getClaimAsString("appCode"), jwt.getClaimAsString("app_code")); if (appCode != null && !appCode.isBlank()) { permissions.add("APP_" + appCode.toUpperCase(java.util.Locale.ROOT)); } String username = firstNonBlank(jwt.getClaimAsString("username"), jwt.getClaimAsString("preferred_username"), jwt.getSubject()); Long userId = longClaim(jwt, "userId"); if (userId == null) userId = longClaim(jwt, "user_id"); AuthenticatedUser user = new AuthenticatedUser( userId == null ? longValue(jwt.getSubject()) : userId, longClaimEither(jwt, "tenantId", "tenant_id"), longClaim(jwt, "operator_tenant_id"), longClaim(jwt, "membership_id"), username, appCode, jwt.getClaimAsString("client_type"), permissions, Boolean.TRUE.equals(jwt.getClaim("impersonated")), longClaim(jwt, "actor_user_id"), longClaim(jwt, "actor_membership_id"), Boolean.TRUE.equals(jwt.getClaim("password_change_required"))); List authorities = permissions.stream() .map(SimpleGrantedAuthority::new).toList(); return new BaiyueJwtAuthenticationToken(user, jwt.getTokenValue(), authorities); } private void addClaimValues(Set target, Object value) { if (value instanceof Collection collection) { collection.stream().map(Object::toString).filter(s -> !s.isBlank()).forEach(target::add); } else if (value instanceof String text) { for (String item : text.split("[ ,]+")) { if (!item.isBlank()) target.add(item); } } } private void addRoles(Set target, Object value) { Set roles = new LinkedHashSet<>(); addClaimValues(roles, value); roles.stream().map(role -> role.startsWith("ROLE_") ? role : "ROLE_" + role).forEach(target::add); } private Long longValue(String value) { if (value == null || value.isBlank()) return null; try { return Long.valueOf(value); } catch (NumberFormatException ignored) { return null; } } private Long longClaim(Jwt jwt, String name) { Object value = jwt.getClaims().get(name); if (value instanceof Number number) return number.longValue(); if (value instanceof String text) return longValue(text); return null; } private Long longClaimEither(Jwt jwt, String first, String second) { Long value = longClaim(jwt, first); return value == null ? longClaim(jwt, second) : value; } private String firstNonBlank(String... values) { for (String value : values) if (value != null && !value.isBlank()) return value; return "unknown"; } }