Skip to content

Commit 666f175

Browse files
committed
LazyCsrfTokenRepository#loadToken Supports Deferring Delegation
Previously LazyCsrfTokenRepository supported lazily saving the CsrfToken which allowed for lazily saving the CsrfToken. However, it did not support lazily reading the CsrfToken. This meant every request required reading the CsrfToken (often the HttpSession). This commit allows for lazily reading the CsrfToken and thus prevents unnecessary reads to the HttpSession. Closes gh-11700
1 parent 002a770 commit 666f175

File tree

2 files changed

+75
-0
lines changed

2 files changed

+75
-0
lines changed

Diff for: web/src/main/java/org/springframework/security/web/csrf/LazyCsrfTokenRepository.java

+63
Original file line numberDiff line numberDiff line change
@@ -38,6 +38,8 @@ public final class LazyCsrfTokenRepository implements CsrfTokenRepository {
3838

3939
private final CsrfTokenRepository delegate;
4040

41+
private boolean deferLoadToken;
42+
4143
/**
4244
* Creates a new instance
4345
* @param delegate the {@link CsrfTokenRepository} to use. Cannot be null
@@ -48,6 +50,15 @@ public LazyCsrfTokenRepository(CsrfTokenRepository delegate) {
4850
this.delegate = delegate;
4951
}
5052

53+
/**
54+
* Determines if {@link #loadToken(HttpServletRequest)} should be lazily loaded.
55+
* @param deferLoadToken true if should lazily load
56+
* {@link #loadToken(HttpServletRequest)}. Default false.
57+
*/
58+
public void setDeferLoadToken(boolean deferLoadToken) {
59+
this.deferLoadToken = deferLoadToken;
60+
}
61+
5162
/**
5263
* Generates a new token
5364
* @param request the {@link HttpServletRequest} to use. The
@@ -77,6 +88,9 @@ public void saveToken(CsrfToken token, HttpServletRequest request, HttpServletRe
7788
*/
7889
@Override
7990
public CsrfToken loadToken(HttpServletRequest request) {
91+
if (this.deferLoadToken) {
92+
return new LazyLoadCsrfToken(request, this.delegate);
93+
}
8094
return this.delegate.loadToken(request);
8195
}
8296

@@ -92,6 +106,55 @@ private HttpServletResponse getResponse(HttpServletRequest request) {
92106
return response;
93107
}
94108

109+
private final class LazyLoadCsrfToken implements CsrfToken {
110+
111+
private final HttpServletRequest request;
112+
113+
private final CsrfTokenRepository tokenRepository;
114+
115+
private CsrfToken token;
116+
117+
private LazyLoadCsrfToken(HttpServletRequest request, CsrfTokenRepository tokenRepository) {
118+
this.request = request;
119+
this.tokenRepository = tokenRepository;
120+
}
121+
122+
private CsrfToken getDelegate() {
123+
if (this.token != null) {
124+
return this.token;
125+
}
126+
// load from the delegate repository
127+
this.token = LazyCsrfTokenRepository.this.delegate.loadToken(this.request);
128+
if (this.token == null) {
129+
// return a generated token that is lazily saved since
130+
// LazyCsrfTokenRepository#loadToken always returns a value
131+
this.token = generateToken(this.request);
132+
}
133+
return this.token;
134+
}
135+
136+
@Override
137+
public String getHeaderName() {
138+
return getDelegate().getHeaderName();
139+
}
140+
141+
@Override
142+
public String getParameterName() {
143+
return getDelegate().getParameterName();
144+
}
145+
146+
@Override
147+
public String getToken() {
148+
return getDelegate().getToken();
149+
}
150+
151+
@Override
152+
public String toString() {
153+
return "LazyLoadCsrfToken{" + "token=" + this.token + '}';
154+
}
155+
156+
}
157+
95158
private static final class SaveOnAccessCsrfToken implements CsrfToken {
96159

97160
private transient CsrfTokenRepository tokenRepository;

Diff for: web/src/test/java/org/springframework/security/web/csrf/LazyCsrfTokenRepositoryTests.java

+12
Original file line numberDiff line numberDiff line change
@@ -31,6 +31,7 @@
3131
import static org.mockito.BDDMockito.given;
3232
import static org.mockito.Mockito.mock;
3333
import static org.mockito.Mockito.verify;
34+
import static org.mockito.Mockito.verifyNoInteractions;
3435
import static org.mockito.Mockito.verifyZeroInteractions;
3536

3637
/**
@@ -98,4 +99,15 @@ public void loadTokenDelegates() {
9899
verify(this.delegate).loadToken(this.request);
99100
}
100101

102+
@Test
103+
public void loadTokenWhenDeferLoadToken() {
104+
given(this.delegate.loadToken(this.request)).willReturn(this.token);
105+
this.repository.setDeferLoadToken(true);
106+
CsrfToken loadToken = this.repository.loadToken(this.request);
107+
verifyNoInteractions(this.delegate);
108+
assertThat(loadToken.getToken()).isEqualTo(this.token.getToken());
109+
assertThat(loadToken.getHeaderName()).isEqualTo(this.token.getHeaderName());
110+
assertThat(loadToken.getParameterName()).isEqualTo(this.token.getParameterName());
111+
}
112+
101113
}

0 commit comments

Comments
 (0)