mirror of
https://github.com/spring-projects/spring-security.git
synced 2026-04-21 16:30:27 +00:00
Merge branch '7.0.x' into 7.1.x
This commit is contained in:
commit
09b8945f01
@ -153,7 +153,9 @@ public final class JdbcOneTimeTokenService implements OneTimeTokenService, Dispo
|
||||
return null;
|
||||
}
|
||||
OneTimeToken token = tokens.get(0);
|
||||
deleteOneTimeToken(token);
|
||||
if (deleteOneTimeToken(token) == 0) {
|
||||
return null;
|
||||
}
|
||||
if (isExpired(token)) {
|
||||
return null;
|
||||
}
|
||||
@ -171,11 +173,11 @@ public final class JdbcOneTimeTokenService implements OneTimeTokenService, Dispo
|
||||
return this.jdbcOperations.query(SELECT_ONE_TIME_TOKEN_SQL, pss, this.oneTimeTokenRowMapper);
|
||||
}
|
||||
|
||||
private void deleteOneTimeToken(OneTimeToken oneTimeToken) {
|
||||
private int deleteOneTimeToken(OneTimeToken oneTimeToken) {
|
||||
List<SqlParameterValue> parameters = List
|
||||
.of(new SqlParameterValue(Types.VARCHAR, oneTimeToken.getTokenValue()));
|
||||
PreparedStatementSetter pss = new ArgumentPreparedStatementSetter(parameters.toArray());
|
||||
this.jdbcOperations.update(DELETE_ONE_TIME_TOKEN_SQL, pss);
|
||||
return this.jdbcOperations.update(DELETE_ONE_TIME_TOKEN_SQL, pss);
|
||||
}
|
||||
|
||||
private @Nullable ThreadPoolTaskScheduler createTaskScheduler(String cleanupCron) {
|
||||
|
||||
@ -21,19 +21,24 @@ import java.time.Duration;
|
||||
import java.time.Instant;
|
||||
import java.time.ZoneOffset;
|
||||
import java.time.temporal.ChronoUnit;
|
||||
import java.util.List;
|
||||
|
||||
import org.junit.jupiter.api.AfterEach;
|
||||
import org.junit.jupiter.api.BeforeEach;
|
||||
import org.junit.jupiter.api.Test;
|
||||
import org.mockito.ArgumentMatchers;
|
||||
|
||||
import org.springframework.jdbc.core.JdbcOperations;
|
||||
import org.springframework.jdbc.core.JdbcTemplate;
|
||||
import org.springframework.jdbc.core.PreparedStatementSetter;
|
||||
import org.springframework.jdbc.core.RowMapper;
|
||||
import org.springframework.jdbc.datasource.embedded.EmbeddedDatabase;
|
||||
import org.springframework.jdbc.datasource.embedded.EmbeddedDatabaseBuilder;
|
||||
import org.springframework.jdbc.datasource.embedded.EmbeddedDatabaseType;
|
||||
|
||||
import static org.assertj.core.api.Assertions.assertThat;
|
||||
import static org.assertj.core.api.Assertions.assertThatIllegalArgumentException;
|
||||
import static org.mockito.ArgumentMatchers.any;
|
||||
import static org.mockito.BDDMockito.given;
|
||||
import static org.mockito.Mockito.mock;
|
||||
|
||||
@ -145,6 +150,27 @@ class JdbcOneTimeTokenServiceTests {
|
||||
assertThat(consumedOneTimeToken).isNull();
|
||||
}
|
||||
|
||||
@Test
|
||||
void consumeWhenTokenIsDeletedConcurrentlyThenReturnNull() throws Exception {
|
||||
// Simulates a concurrent consume: SELECT finds the token but DELETE affects
|
||||
// 0 rows because another caller already consumed it.
|
||||
JdbcOperations jdbcOperations = mock(JdbcOperations.class);
|
||||
Instant notExpired = Instant.now().plus(5, ChronoUnit.MINUTES);
|
||||
OneTimeToken token = new DefaultOneTimeToken(TOKEN_VALUE, USERNAME, notExpired);
|
||||
given(jdbcOperations.query(any(String.class), any(PreparedStatementSetter.class),
|
||||
ArgumentMatchers.<RowMapper<OneTimeToken>>any()))
|
||||
.willReturn(List.of(token));
|
||||
given(jdbcOperations.update(any(String.class), any(PreparedStatementSetter.class))).willReturn(0);
|
||||
JdbcOneTimeTokenService service = new JdbcOneTimeTokenService(jdbcOperations);
|
||||
try {
|
||||
OneTimeToken consumed = service.consume(new OneTimeTokenAuthenticationToken(TOKEN_VALUE));
|
||||
assertThat(consumed).isNull();
|
||||
}
|
||||
finally {
|
||||
service.destroy();
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
void consumeWhenTokenIsExpiredThenReturnNull() {
|
||||
GenerateOneTimeTokenRequest request = new GenerateOneTimeTokenRequest(USERNAME);
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user