- SessionCleanupJob(@Scheduled,间隔/初始延迟/保留期均配置项,默认 6h/10m/30d)删除撤销或过期超过保留期的会话行;保留期即重用检测窗口(已撤销行是重用比对对象),注释与 sample 已说明 - 多实例并发安全(幂等 DELETE);轮换链 FK 由 ON DELETE SET NULL 释放 - 门禁:./mvnw clean test → BUILD SUCCESS,75 测试 0 失败(新增 SessionCleanupIntegrationTest:仅删超期死亡行,存活与近期撤销行保留) Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
This commit is contained in:
+79
@@ -0,0 +1,79 @@
|
||||
package com.patbond.patbond.user.session;
|
||||
|
||||
import com.patbond.patbond.common.session.CreateSessionRequest;
|
||||
import com.patbond.patbond.common.session.SessionTokens;
|
||||
import com.patbond.patbond.common.user.CreateUserRequest;
|
||||
import com.patbond.patbond.user.TestcontainersConfiguration;
|
||||
import com.patbond.patbond.user.service.UserService;
|
||||
import org.junit.jupiter.api.Test;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.boot.test.context.SpringBootTest;
|
||||
import org.springframework.context.annotation.Import;
|
||||
import org.springframework.jdbc.core.simple.JdbcClient;
|
||||
|
||||
import java.util.List;
|
||||
import java.util.UUID;
|
||||
|
||||
import static org.assertj.core.api.Assertions.assertThat;
|
||||
|
||||
/**
|
||||
* The cleanup job removes rows dead longer than the retention window and
|
||||
* nothing else: live sessions and recently revoked rows (still needed for
|
||||
* reuse detection) survive.
|
||||
*/
|
||||
@SpringBootTest
|
||||
@Import(TestcontainersConfiguration.class)
|
||||
class SessionCleanupIntegrationTest {
|
||||
|
||||
@Autowired
|
||||
private UserService userService;
|
||||
|
||||
@Autowired
|
||||
private SessionService sessionService;
|
||||
|
||||
@Autowired
|
||||
private SessionCleanupJob cleanupJob;
|
||||
|
||||
@Autowired
|
||||
private JdbcClient jdbcClient;
|
||||
|
||||
@Test
|
||||
void removesOnlySessionsDeadLongerThanRetention() {
|
||||
UUID userId = userService.createUser(
|
||||
new CreateUserRequest("cleanup_user", "secret123", null, null)).getId();
|
||||
|
||||
SessionTokens live = createSession(userId);
|
||||
SessionTokens recentlyRevoked = createSession(userId);
|
||||
SessionTokens longRevoked = createSession(userId);
|
||||
SessionTokens longExpired = createSession(userId);
|
||||
|
||||
sessionService.revoke(userId, recentlyRevoked.getRefreshToken());
|
||||
sessionService.revoke(userId, longRevoked.getRefreshToken());
|
||||
// Backdate beyond the 30-day retention (created_at moves along so the
|
||||
// ck_sessions_expiry / ck_sessions_revoked_at ordering checks hold).
|
||||
backdate("created_at = now() - interval '40 days', revoked_at = now() - interval '31 days'",
|
||||
longRevoked.getSessionId());
|
||||
backdate("created_at = now() - interval '61 days', expires_at = now() - interval '31 days'",
|
||||
longExpired.getSessionId());
|
||||
|
||||
cleanupJob.cleanUp();
|
||||
|
||||
List<UUID> remaining = jdbcClient.sql(
|
||||
"SELECT id FROM identity.auth_sessions WHERE user_id = :userId")
|
||||
.param("userId", userId)
|
||||
.query(UUID.class)
|
||||
.list();
|
||||
assertThat(remaining)
|
||||
.containsExactlyInAnyOrder(live.getSessionId(), recentlyRevoked.getSessionId());
|
||||
}
|
||||
|
||||
private SessionTokens createSession(UUID userId) {
|
||||
return sessionService.create(new CreateSessionRequest(userId, null, null, null));
|
||||
}
|
||||
|
||||
private void backdate(String setClause, UUID sessionId) {
|
||||
jdbcClient.sql("UPDATE identity.auth_sessions SET " + setClause + " WHERE id = :id")
|
||||
.param("id", sessionId)
|
||||
.update();
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user