打印箱唛修改 尾程垮桥事件提交
This commit is contained in:
+12
-5
@@ -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
|
||||
|
||||
+16
@@ -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; }
|
||||
}
|
||||
+1
-1
@@ -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)
|
||||
|
||||
+32
@@ -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
@@ -1,2 +1,3 @@
|
||||
com.baiyue.common.security.HmacJwtDecoderAutoConfiguration
|
||||
com.baiyue.common.security.JwksJwtDecoderAutoConfiguration
|
||||
com.baiyue.common.security.BaiyueSecurityAutoConfiguration
|
||||
|
||||
Reference in New Issue
Block a user