fix: restore shared HS256 authentication

This commit is contained in:
2026-08-13 17:58:26 +08:00
parent 9eaaa9a0a0
commit f779ab56dd
7 changed files with 10 additions and 51 deletions
@@ -1,8 +1,6 @@
package com.baiyue.auth.config;
import com.nimbusds.jose.jwk.JWKSet;
import com.nimbusds.jose.jwk.RSAKey;
import com.nimbusds.jose.jwk.source.ImmutableJWKSet;
import com.nimbusds.jose.jwk.source.ImmutableSecret;
import com.baiyue.common.security.JwtAuthenticationFilter;
import org.springframework.boot.context.properties.EnableConfigurationProperties;
import org.springframework.context.annotation.Bean;
@@ -33,7 +31,6 @@ public class AuthSecurityConfig {
"/api/auth/refresh",
"/api/auth/logout",
"/api/auth/impersonations/exchange",
"/api/auth/.well-known/jwks.json",
"/actuator/health",
"/v3/api-docs/**",
"/swagger-ui/**",
@@ -45,19 +42,9 @@ public class AuthSecurityConfig {
@Bean
JwtEncoder jwtEncoder(AuthSecurityProperties properties) {
try {
JWKSet set = JWKSet.parse(properties.rsaJwkSet());
RSAKey current = set.getKeyByKeyId(properties.currentKid()) instanceof RSAKey rsa ? rsa : null;
if (current == null || !current.isPrivate()) {
throw new IllegalStateException("current-kid 必须指向一把包含私钥的 RSA 签名密钥");
}
if (set.getKeys().stream().anyMatch(jwk -> !(jwk instanceof RSAKey))) {
throw new IllegalStateException("RSA JWK Set 只能包含 RSA 密钥");
}
return new NimbusJwtEncoder(new ImmutableJWKSet<>(set));
} catch (java.text.ParseException exception) {
throw new IllegalStateException("RSA JWK Set配置无效", exception);
}
byte[] secret = properties.jwtSecret().getBytes(StandardCharsets.UTF_8);
if (secret.length < 32) throw new IllegalStateException("JWT_SECRET 至少需要 32 字节");
return new NimbusJwtEncoder(new ImmutableSecret<>(secret));
}
@Bean
@@ -9,7 +9,6 @@ import java.time.Duration;
*/
@ConfigurationProperties(prefix = "baiyue.security")
public record AuthSecurityProperties(String jwtSecret, String issuer, String audience,
String rsaJwkSet, String currentKid,
Duration accessTokenTtl, Duration refreshTokenTtl,
int maxLoginFailures, Duration lockDuration) {
}
@@ -1,26 +0,0 @@
package com.baiyue.auth.controller;
import com.baiyue.auth.config.AuthSecurityProperties;
import com.nimbusds.jose.jwk.JWKSet;
import org.springframework.http.CacheControl;
import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RestController;
import java.util.Map;
import java.util.concurrent.TimeUnit;
/** 仅发布 RSA 公钥;轮换期配置当前与上一把公钥。 */
@RestController
public class JwksController {
private final AuthSecurityProperties properties;
public JwksController(AuthSecurityProperties properties) { this.properties = properties; }
@GetMapping("/api/auth/.well-known/jwks.json")
public ResponseEntity<Map<String, Object>> jwks() throws Exception {
JWKSet publicSet = JWKSet.parse(properties.rsaJwkSet()).toPublicJWKSet();
return ResponseEntity.ok()
.cacheControl(CacheControl.maxAge(5, TimeUnit.MINUTES).cachePublic())
.body(publicSet.toJSONObject());
}
}
@@ -16,7 +16,7 @@ import com.baiyue.common.core.exception.BizException;
import jakarta.annotation.Resource;
import org.springframework.security.crypto.password.PasswordEncoder;
import org.springframework.security.oauth2.jose.jws.SignatureAlgorithm;
import org.springframework.security.oauth2.jose.jws.MacAlgorithm;
import org.springframework.security.oauth2.jwt.JwtClaimsSet;
import org.springframework.security.oauth2.jwt.JwtEncoder;
import org.springframework.security.oauth2.jwt.JwtEncoderParameters;
@@ -198,7 +198,7 @@ public class AuthService {
public record ImpersonationTokenResponse(String tokenType, String accessToken, long expiresIn) { }
private JwsHeader signingHeader() {
return JwsHeader.with(SignatureAlgorithm.RS256).keyId(properties.currentKid()).build();
return JwsHeader.with(MacAlgorithm.HS256).build();
}
private static BizException invalidCredentials() {
-2
View File
@@ -31,8 +31,6 @@ spring:
encode: UTF-8
baiyue.security:
jwt-secret: ${JWT_SECRET:change-me-to-at-least-32-bytes-long-before-start}
rsa-jwk-set: ${JWT_RSA_JWK_SET:}
current-kid: ${JWT_CURRENT_KID:}
internal-service-token: ${INTERNAL_SERVICE_TOKEN:change-me-to-an-internal-token-at-least-32-bytes-long}
token-key-prefix: ${REDIS_TOKEN_KEY_PREFIX:baiyue:auth}
issuer: ${JWT_ISSUER:auth-service}
@@ -12,6 +12,7 @@ import org.springframework.boot.test.mock.mockito.MockBean;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.security.crypto.password.PasswordEncoder;
import org.springframework.security.oauth2.jose.jws.MacAlgorithm;
import org.springframework.security.oauth2.jwt.Jwt;
import org.springframework.security.oauth2.jwt.JwtEncoder;
import org.springframework.security.oauth2.jwt.JwtEncoderParameters;
@@ -39,7 +40,7 @@ class AuthServiceSmallJwtTest {
static class TestConfig {
@Bean
AuthSecurityProperties properties() {
return new AuthSecurityProperties("unused", "auth-service", "lastleg-api", "{}", "current-2026-08",
return new AuthSecurityProperties("unused", "auth-service", "lastleg-api",
Duration.ofMinutes(30),
Duration.ofDays(7), 5, Duration.ofMinutes(10));
}
@@ -82,7 +83,8 @@ class AuthServiceSmallJwtTest {
.containsEntry("membership_id", 84L)
.doesNotContainKeys("permissions", "scope");
assertThat(jwt.getValue().getClaims().getAudience()).containsExactly("lastleg-api");
assertThat(jwt.getValue().getJwsHeader().getKeyId()).isEqualTo("current-2026-08");
assertThat(jwt.getValue().getJwsHeader().getAlgorithm()).isEqualTo(MacAlgorithm.HS256);
assertThat(jwt.getValue().getJwsHeader().getKeyId()).isNull();
verify(tokenSessionStore).store(any(), org.mockito.ArgumentMatchers.eq(42L),
org.mockito.ArgumentMatchers.eq(Set.of("PLATFORM_ADMIN")),
org.mockito.ArgumentMatchers.eq(Set.of("platform:order:list")), any(),
-1
View File
@@ -7,6 +7,5 @@ spring:
baiyue:
security:
audience: ${JWT_AUDIENCE:lastleg-api}
current-kid: ${JWT_CURRENT_KID:}
access-token-ttl: ${ACCESS_TOKEN_TTL:15m}
refresh-token-ttl: ${REFRESH_TOKEN_TTL:7d}