Skip to content

Commit 612b15a

Browse files
committed
JdbcOneTimeTokenService.setCleanupCron
Spring Security uses setter methods for optional member variables. Allows for a null cleanupCron to disable the cleanup. In a clustered environment it is likely that users do not want all nodes to be performing a cleanup because it will cause contention on the ott table. Another example is if a user wants to invoke cleanUpExpiredTokens with a different strategy all together, they might want to disable the cron job. Issue gh-15735
1 parent 4787ac2 commit 612b15a

File tree

2 files changed

+36
-13
lines changed

2 files changed

+36
-13
lines changed

core/src/main/java/org/springframework/security/authentication/ott/JdbcOneTimeTokenService.java

+25-13
Original file line numberDiff line numberDiff line change
@@ -31,6 +31,7 @@
3131
import org.apache.commons.logging.LogFactory;
3232

3333
import org.springframework.beans.factory.DisposableBean;
34+
import org.springframework.beans.factory.InitializingBean;
3435
import org.springframework.jdbc.core.ArgumentPreparedStatementSetter;
3536
import org.springframework.jdbc.core.JdbcOperations;
3637
import org.springframework.jdbc.core.PreparedStatementSetter;
@@ -40,7 +41,6 @@
4041
import org.springframework.scheduling.support.CronTrigger;
4142
import org.springframework.util.Assert;
4243
import org.springframework.util.CollectionUtils;
43-
import org.springframework.util.StringUtils;
4444

4545
/**
4646
*
@@ -56,7 +56,7 @@
5656
* @author Max Batischev
5757
* @since 6.4
5858
*/
59-
public final class JdbcOneTimeTokenService implements OneTimeTokenService, DisposableBean {
59+
public final class JdbcOneTimeTokenService implements OneTimeTokenService, DisposableBean, InitializingBean {
6060

6161
private final Log logger = LogFactory.getLog(getClass());
6262

@@ -70,7 +70,7 @@ public final class JdbcOneTimeTokenService implements OneTimeTokenService, Dispo
7070

7171
private ThreadPoolTaskScheduler taskScheduler;
7272

73-
private static final String DEFAULT_CLEANUP_CRON = "0 * * * * *";
73+
private static final String DEFAULT_CLEANUP_CRON = "@hourly";
7474

7575
private static final String TABLE_NAME = "one_time_tokens";
7676

@@ -104,23 +104,27 @@ public final class JdbcOneTimeTokenService implements OneTimeTokenService, Dispo
104104
/**
105105
* Constructs a {@code JdbcOneTimeTokenService} using the provide parameters.
106106
* @param jdbcOperations the JDBC operations
107-
* @param cleanupCron cleanup cron expression
108107
*/
109-
public JdbcOneTimeTokenService(JdbcOperations jdbcOperations, String cleanupCron) {
110-
Assert.isTrue(StringUtils.hasText(cleanupCron), "cleanupCron cannot be null orr empty");
108+
public JdbcOneTimeTokenService(JdbcOperations jdbcOperations) {
111109
Assert.notNull(jdbcOperations, "jdbcOperations cannot be null");
112110
this.jdbcOperations = jdbcOperations;
113-
this.taskScheduler = createTaskScheduler(cleanupCron);
111+
this.taskScheduler = createTaskScheduler(DEFAULT_CLEANUP_CRON);
114112
}
115113

116114
/**
117-
* Constructs a {@code JdbcOneTimeTokenService} using the provide parameters.
118-
* @param jdbcOperations the JDBC operations
115+
* Sets the chron expression used for cleaning up expired sessions. The default is to
116+
* run hourly.
117+
*
118+
* For more advanced use cases the cleanupCron may be set to null which will disable
119+
* the built-in cleanup. Users can then invoke {@link #cleanupExpiredTokens()} using
120+
* custom logic.
121+
* @param cleanupCron the chron expression passed to {@link CronTrigger} used for
122+
* determining how frequent to perform cleanup. The default is "@hourly".
123+
* @see CronTrigger
124+
* @see #cleanupExpiredTokens()
119125
*/
120-
public JdbcOneTimeTokenService(JdbcOperations jdbcOperations) {
121-
Assert.notNull(jdbcOperations, "jdbcOperations cannot be null");
122-
this.jdbcOperations = jdbcOperations;
123-
this.taskScheduler = createTaskScheduler(DEFAULT_CLEANUP_CRON);
126+
public void setCleanupCron(String cleanupCron) {
127+
this.taskScheduler = createTaskScheduler(cleanupCron);
124128
}
125129

126130
@Override
@@ -174,6 +178,9 @@ private void deleteOneTimeToken(OneTimeToken oneTimeToken) {
174178
}
175179

176180
private ThreadPoolTaskScheduler createTaskScheduler(String cleanupCron) {
181+
if (cleanupCron == null) {
182+
return null;
183+
}
177184
ThreadPoolTaskScheduler taskScheduler = new ThreadPoolTaskScheduler();
178185
taskScheduler.setThreadNamePrefix("spring-one-time-tokens-");
179186
taskScheduler.initialize();
@@ -188,6 +195,11 @@ public void cleanupExpiredTokens() {
188195
this.logger.debug("Cleaned up " + deletedCount + " expired tokens");
189196
}
190197

198+
@Override
199+
public void afterPropertiesSet() throws Exception {
200+
this.taskScheduler.afterPropertiesSet();
201+
}
202+
191203
@Override
192204
public void destroy() throws Exception {
193205
if (this.taskScheduler != null) {

core/src/test/java/org/springframework/security/authentication/ott/JdbcOneTimeTokenServiceTests.java

+11
Original file line numberDiff line numberDiff line change
@@ -175,6 +175,17 @@ void cleanupExpiredTokens() {
175175
assertThat(deletedOneTimeToken2).isNull();
176176
}
177177

178+
@Test
179+
void setCleanupChronWhenNullThenNoException() {
180+
this.oneTimeTokenService.setCleanupCron(null);
181+
}
182+
183+
@Test
184+
void setCleanupChronWhenAlreadyNullThenNoException() {
185+
this.oneTimeTokenService.setCleanupCron(null);
186+
this.oneTimeTokenService.setCleanupCron(null);
187+
}
188+
178189
private void saveToken(OneTimeToken oneTimeToken) {
179190
List<SqlParameterValue> parameters = this.oneTimeTokenParametersMapper.apply(oneTimeToken);
180191
PreparedStatementSetter pss = new ArgumentPreparedStatementSetter(parameters.toArray());

0 commit comments

Comments
 (0)