Files
baiyue-firstleg/common-security/src/main/java/com/baiyue/common/security/JwtClaimsMapper.java
T

90 lines
3.7 KiB
Java

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<String> 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<SimpleGrantedAuthority> authorities = permissions.stream()
.map(SimpleGrantedAuthority::new).toList();
return new BaiyueJwtAuthenticationToken(user, jwt.getTokenValue(), authorities);
}
private void addClaimValues(Set<String> 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<String> target, Object value) {
Set<String> 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";
}
}