打印箱唛修改 尾程垮桥事件提交

This commit is contained in:
2026-08-13 17:13:58 +08:00
parent b004edaf9e
commit c43d581ba6
59 changed files with 1609 additions and 96 deletions
@@ -23,7 +23,7 @@ import org.springframework.security.web.authentication.UsernamePasswordAuthentic
* Servlet 服务安全自动配置,建立无状态 JWT 认证链、统一异常响应和审计操作者解析。
*/
@AutoConfiguration(
after = HmacJwtDecoderAutoConfiguration.class,
after = {HmacJwtDecoderAutoConfiguration.class, JwksJwtDecoderAutoConfiguration.class},
before = SecurityAutoConfiguration.class)
@EnableMethodSecurity
public class BaiyueSecurityAutoConfiguration {
@@ -47,11 +47,18 @@ public class BaiyueSecurityAutoConfiguration {
@ConditionalOnMissingBean
JwtAuthenticationFilter jwtAuthenticationFilter(JwtDecoder jwtDecoder, JwtClaimsMapper claimsMapper,
AuthenticationEntryPoint entryPoint,
StringRedisTemplate redisTemplate,
org.springframework.beans.factory.ObjectProvider<StringRedisTemplate> redis,
ObjectMapper objectMapper,
@Value("${baiyue.security.token-key-prefix:baiyue:auth}") String keyPrefix) {
return new JwtAuthenticationFilter(jwtDecoder, claimsMapper, entryPoint,
new RedisAccessTokenSessionValidator(redisTemplate, keyPrefix, objectMapper));
@Value("${baiyue.security.token-key-prefix:baiyue:auth}") String keyPrefix,
@Value("${baiyue.security.session-mode:redis}") String sessionMode) {
AccessTokenSessionValidator validator;
if ("claims".equalsIgnoreCase(sessionMode)) validator = new ClaimsAccessTokenSessionValidator();
else {
StringRedisTemplate template=redis.getIfAvailable();
if(template==null) throw new IllegalStateException("session-mode=redis requires StringRedisTemplate");
validator=new RedisAccessTokenSessionValidator(template,keyPrefix,objectMapper);
}
return new JwtAuthenticationFilter(jwtDecoder, claimsMapper, entryPoint, validator);
}
@Bean
@@ -0,0 +1,16 @@
package com.baiyue.common.security;
import org.springframework.security.oauth2.jwt.Jwt;
import java.util.LinkedHashSet;
import java.util.Set;
/** 海外模式直接使用已由 RS256/JWKS 验证的短时令牌授权快照。 */
public class ClaimsAccessTokenSessionValidator implements AccessTokenSessionValidator {
@Override public AccessTokenSession validate(Jwt jwt) {
Long userId=number(jwt.getClaims().getOrDefault("userId",jwt.getClaims().get("user_id")));
return new AccessTokenSession(1,userId,values(jwt.getClaims().get("roles")),
values(jwt.getClaims().getOrDefault("authorities",jwt.getClaims().get("permissions"))));
}
private static Long number(Object value){ if(value instanceof Number n)return n.longValue(); try{return Long.valueOf(String.valueOf(value));}catch(Exception e){return null;} }
private static Set<String> values(Object value){ Set<String> result=new LinkedHashSet<>(); if(value instanceof Iterable<?> it)it.forEach(v->result.add(String.valueOf(v))); else if(value!=null)for(String v:String.valueOf(value).split("[ ,]+"))if(!v.isBlank())result.add(v); return result; }
}
@@ -17,7 +17,7 @@ import java.nio.charset.StandardCharsets;
* HMAC JWT 解码器自动配置,按共享密钥和签发方构建统一令牌验证器。
*/
@AutoConfiguration(before = BaiyueSecurityAutoConfiguration.class)
@ConditionalOnProperty(name = "baiyue.security.jwt-secret")
@ConditionalOnProperty(name = "baiyue.security.jwt-mode", havingValue = "hmac", matchIfMissing = true)
public class HmacJwtDecoderAutoConfiguration {
@Bean
@ConditionalOnMissingBean(JwtDecoder.class)
@@ -0,0 +1,32 @@
package com.baiyue.common.security;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.boot.autoconfigure.AutoConfiguration;
import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean;
import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
import org.springframework.context.annotation.Bean;
import org.springframework.security.oauth2.jwt.JwtDecoder;
import org.springframework.security.oauth2.jwt.JwtValidators;
import org.springframework.security.oauth2.jwt.NimbusJwtDecoder;
import org.springframework.security.oauth2.jwt.JwtClaimValidator;
import org.springframework.security.oauth2.core.DelegatingOAuth2TokenValidator;
import org.springframework.security.oauth2.jose.jws.SignatureAlgorithm;
import java.util.List;
/** 海外服务仅通过可轮换 JWKS 验证 RS256,不访问国内 Redis。 */
@AutoConfiguration(before = BaiyueSecurityAutoConfiguration.class)
@ConditionalOnProperty(name="baiyue.security.jwt-mode",havingValue="jwks")
public class JwksJwtDecoderAutoConfiguration {
@Bean @ConditionalOnMissingBean(JwtDecoder.class)
JwtDecoder jwksJwtDecoder(@Value("${baiyue.security.jwk-set-uri}") String uri,
@Value("${baiyue.security.issuer:auth-service}") String issuer,
@Value("${baiyue.security.audience:lastleg-api}") String audience) {
NimbusJwtDecoder decoder = NimbusJwtDecoder.withJwkSetUri(uri)
.jwsAlgorithm(SignatureAlgorithm.RS256).build();
decoder.setJwtValidator(new DelegatingOAuth2TokenValidator<>(
JwtValidators.createDefaultWithIssuer(issuer),
new JwtClaimValidator<List<String>>("aud", values -> values != null && values.contains(audience))));
return decoder;
}
}
@@ -23,20 +23,21 @@ public class JwtClaimsMapper {
addRoles(permissions, session.roles());
}
String appCode = jwt.getClaimAsString("app_code");
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, "user_id");
Long userId = longClaim(jwt, "userId");
if (userId == null) userId = longClaim(jwt, "user_id");
AuthenticatedUser user = new AuthenticatedUser(
userId == null ? longValue(jwt.getSubject()) : userId,
longClaim(jwt, "tenant_id"),
longClaimEither(jwt, "tenantId", "tenant_id"),
longClaim(jwt, "operator_tenant_id"),
longClaim(jwt, "membership_id"),
username,
jwt.getClaimAsString("app_code"),
appCode,
jwt.getClaimAsString("client_type"),
permissions,
Boolean.TRUE.equals(jwt.getClaim("impersonated")),
@@ -76,6 +77,10 @@ public class JwtClaimsMapper {
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;
@@ -1,2 +1,3 @@
com.baiyue.common.security.HmacJwtDecoderAutoConfiguration
com.baiyue.common.security.JwksJwtDecoderAutoConfiguration
com.baiyue.common.security.BaiyueSecurityAutoConfiguration