轻松聘AI开发
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 
 

88 lines
3.8 KiB

package com.mydreamjob.auth;
import com.mydreamjob.config.AppJwtProperties;
import org.junit.jupiter.api.Test;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.boot.test.mock.mockito.MockBean;
import org.springframework.context.ApplicationContext;
import org.springframework.data.redis.core.RedisTemplate;
import java.nio.file.Files;
import java.nio.file.Path;
import java.time.Instant;
import static org.assertj.core.api.Assertions.assertThat;
/**
* S02 复核项:只配置三个新密钥、**不配置旧 JWT_SECRET** 时应用仍可启动(任务书 §五)。
*
* <p>旧 {@code app.jwt.secret} 对 S02 的 Token 已无任何作用,但仍被
* {@code talentdiscovery.CandidateKeyGenerator} 使用(该组件在密钥为空时自行失败关闭),
* 因此保留属性但改为非强制解析。本测试证明:
* 1) 旧密钥为空时全量上下文仍能启动;2) 三项 S02 密钥正确绑定;
* 3) {@code application.yml} 中的旧密钥占位符带默认值,不再阻塞启动。</p>
*/
@SpringBootTest(properties = {
"app.jwt.secret=",
"app.jwt.access-secret=test-access-secret-key-for-integration-tests-only-not-for-production",
"app.jwt.refresh-secret=test-refresh-secret-key-for-integration-tests-only-not-for-production",
"app.jwt.token-hash-secret=test-token-hash-secret-key-for-integration-tests-only-not-for-production",
})
@org.springframework.test.context.ActiveProfiles("integration-test")
class JwtSecretOptionalStartupIntegrationTest {
@MockBean
private RedisTemplate<String, Object> redisTemplate;
@Autowired
private ApplicationContext applicationContext;
@Autowired
private AppJwtProperties jwtProperties;
@Autowired
private JwtTokenProvider jwtTokenProvider;
@Autowired
private AuthSessionService authSessionService;
@Test
void contextStartsWithOnlyTheThreeNewSecrets() {
assertThat(applicationContext).isNotNull();
assertThat(jwtTokenProvider).isNotNull();
assertThat(authSessionService).isNotNull();
assertThat(jwtProperties.getSecret())
.as("旧密钥为空也必须能启动").isNullOrEmpty();
assertThat(jwtProperties.getAccessSecret()).isNotBlank();
assertThat(jwtProperties.getRefreshSecret()).isNotBlank();
assertThat(jwtProperties.getTokenHashSecret()).isNotBlank();
assertThat(jwtProperties.getAccessSecret())
.isNotEqualTo(jwtProperties.getRefreshSecret());
}
@Test
void accessAndRefreshNeverFallBackToLegacySecret() throws Exception {
// 旧密钥为空时,S02 的两类 Token 仍必须能正常签发与校验 —— 证明没有回退到它。
Instant now = Instant.now();
String sid = java.util.UUID.randomUUID().toString();
IssuedToken access = jwtTokenProvider.generateAccessToken(
1L, "STUDENT", sid, java.util.UUID.randomUUID().toString(), now);
IssuedToken refresh = jwtTokenProvider.generateRefreshToken(
1L, sid, java.util.UUID.randomUUID().toString(), now);
assertThat(jwtTokenProvider.parseAndValidateAccessToken(access.token(), now).userId()).isEqualTo(1L);
assertThat(jwtTokenProvider.parseAndValidateRefreshToken(refresh.token(), now).userId()).isEqualTo(1L);
}
@Test
void applicationYmlNoLongerForcesLegacySecret() throws Exception {
Path yml = Path.of("src/main/resources/application.yml");
String content = Files.readString(yml);
assertThat(content)
.as("旧密钥占位符必须带默认值,否则未配置 JWT_SECRET 的环境无法启动")
.contains("secret: ${JWT_SECRET:}");
assertThat(content).doesNotContain("secret: ${JWT_SECRET}\n");
}
}