Skip to content

Commit b79ba89

Browse files
kumo829jzheaux
authored andcommitted
Add setCookieCustomizer to csrf token repository
- Mark setCookieHttpOnly, setCookieDomain, setCookieMaxAge and setSecure as deprecated. - Add the method setCookieCustomizer which allows to set properties to the ResponseCookieBuilder without having to add new setter methods. Closes gh-12086
1 parent cd0f02d commit b79ba89

File tree

4 files changed

+263
-77
lines changed

4 files changed

+263
-77
lines changed

web/src/main/java/org/springframework/security/web/csrf/CookieCsrfTokenRepository.java

+34-43
Original file line numberDiff line numberDiff line change
@@ -15,14 +15,15 @@
1515
*/
1616

1717
package org.springframework.security.web.csrf;
18-
1918
import java.util.UUID;
19+
import java.util.function.Consumer;
2020

21-
import jakarta.servlet.ServletRequest;
2221
import jakarta.servlet.http.Cookie;
2322
import jakarta.servlet.http.HttpServletRequest;
2423
import jakarta.servlet.http.HttpServletResponse;
2524

25+
import org.springframework.http.HttpHeaders;
26+
import org.springframework.http.ResponseCookie;
2627
import org.springframework.util.Assert;
2728
import org.springframework.util.StringUtils;
2829
import org.springframework.web.util.WebUtils;
@@ -34,6 +35,7 @@
3435
*
3536
* @author Rob Winch
3637
* @author Steve Riesenberg
38+
* @author Alex Montoya
3739
* @since 4.1
3840
*/
3941
public final class CookieCsrfTokenRepository implements CsrfTokenRepository {
@@ -63,7 +65,17 @@ public final class CookieCsrfTokenRepository implements CsrfTokenRepository {
6365

6466
private int cookieMaxAge = -1;
6567

66-
public CookieCsrfTokenRepository() {
68+
private Consumer<ResponseCookie.ResponseCookieBuilder> cookieCustomizer = (builder) -> {};
69+
70+
/**
71+
* Add a {@link Consumer} for a {@code ResponseCookieBuilder} that will be invoked
72+
* for each cookie being built, just before the call to {@code build()}.
73+
* @param cookieCustomizer consumer for a cookie builder
74+
* @since 6.1
75+
*/
76+
public void setCookieCustomizer(Consumer<ResponseCookie.ResponseCookieBuilder> cookieCustomizer) {
77+
Assert.notNull(cookieCustomizer, "cookieCustomizer must not be null");
78+
this.cookieCustomizer = cookieCustomizer;
6779
}
6880

6981
@Override
@@ -74,15 +86,17 @@ public CsrfToken generateToken(HttpServletRequest request) {
7486
@Override
7587
public void saveToken(CsrfToken token, HttpServletRequest request, HttpServletResponse response) {
7688
String tokenValue = (token != null) ? token.getToken() : "";
77-
Cookie cookie = new Cookie(this.cookieName, tokenValue);
78-
cookie.setSecure((this.secure != null) ? this.secure : request.isSecure());
79-
cookie.setPath(StringUtils.hasLength(this.cookiePath) ? this.cookiePath : this.getRequestContext(request));
80-
cookie.setMaxAge((token != null) ? this.cookieMaxAge : 0);
81-
cookie.setHttpOnly(this.cookieHttpOnly);
82-
if (StringUtils.hasLength(this.cookieDomain)) {
83-
cookie.setDomain(this.cookieDomain);
84-
}
85-
response.addCookie(cookie);
89+
90+
ResponseCookie.ResponseCookieBuilder cookieBuilder = ResponseCookie.from(this.cookieName, tokenValue)
91+
.secure(this.secure != null ? this.secure : request.isSecure())
92+
.path(StringUtils.hasLength(this.cookiePath) ? this.cookiePath : this.getRequestContext(request))
93+
.maxAge(token != null ? this.cookieMaxAge : 0)
94+
.httpOnly(this.cookieHttpOnly)
95+
.domain(this.cookieDomain);
96+
97+
this.cookieCustomizer.accept(cookieBuilder);
98+
99+
response.setHeader(HttpHeaders.SET_COOKIE, cookieBuilder.build().toString());
86100

87101
// Set request attribute to signal that response has blank cookie value,
88102
// which allows loadToken to return null when token has been removed
@@ -143,11 +157,9 @@ public void setCookieName(String cookieName) {
143157
}
144158

145159
/**
146-
* Sets the HttpOnly attribute on the cookie containing the CSRF token. Defaults to
147-
* <code>true</code>.
148-
* @param cookieHttpOnly <code>true</code> sets the HttpOnly attribute,
149-
* <code>false</code> does not set it
160+
* @deprecated Use {@link #setCookieCustomizer(Consumer)} instead.
150161
*/
162+
@Deprecated(since = "6.1")
151163
public void setCookieHttpOnly(boolean cookieHttpOnly) {
152164
this.cookieHttpOnly = cookieHttpOnly;
153165
}
@@ -191,51 +203,30 @@ public String getCookiePath() {
191203
}
192204

193205
/**
194-
* Sets the domain of the cookie that the expected CSRF token is saved to and read
195-
* from.
196-
* @param cookieDomain the domain of the cookie that the expected CSRF token is saved
197-
* to and read from
206+
* @deprecated Use {@link #setCookieCustomizer(Consumer)} instead.
198207
* @since 5.2
199208
*/
209+
@Deprecated(since = "6.1")
200210
public void setCookieDomain(String cookieDomain) {
201211
this.cookieDomain = cookieDomain;
202212
}
203213

204214
/**
205-
* Sets secure flag of the cookie that the expected CSRF token is saved to and read
206-
* from. By default secure flag depends on {@link ServletRequest#isSecure()}
207-
* @param secure the secure flag of the cookie that the expected CSRF token is saved
208-
* to and read from
215+
* @deprecated Use {@link #setCookieCustomizer(Consumer)} instead.
209216
* @since 5.4
210217
*/
218+
@Deprecated(since = "6.1")
211219
public void setSecure(Boolean secure) {
212220
this.secure = secure;
213221
}
214222

215223
/**
216-
* Sets maximum age in seconds for the cookie that the expected CSRF token is saved to
217-
* and read from. By default maximum age value is -1.
218-
*
219-
* <p>
220-
* A positive value indicates that the cookie will expire after that many seconds have
221-
* passed. Note that the value is the <i>maximum</i> age when the cookie will expire,
222-
* not the cookie's current age.
223-
*
224-
* <p>
225-
* A negative value means that the cookie is not stored persistently and will be
226-
* deleted when the Web browser exits.
227-
*
228-
* <p>
229-
* A zero value causes the cookie to be deleted immediately therefore it is not a
230-
* valid value and in that case an {@link IllegalArgumentException} will be thrown.
231-
* @param cookieMaxAge an integer specifying the maximum age of the cookie in seconds;
232-
* if negative, means the cookie is not stored; if zero, the method throws an
233-
* {@link IllegalArgumentException}
224+
* @deprecated Use {@link #setCookieCustomizer(Consumer)} instead.
234225
* @since 5.5
235226
*/
227+
@Deprecated(since = "6.1")
236228
public void setCookieMaxAge(int cookieMaxAge) {
237229
Assert.isTrue(cookieMaxAge != 0, "cookieMaxAge cannot be zero");
238230
this.cookieMaxAge = cookieMaxAge;
239231
}
240-
241232
}

web/src/main/java/org/springframework/security/web/server/csrf/CookieServerCsrfTokenRepository.java

+29-29
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,7 @@
1717
package org.springframework.security.web.server.csrf;
1818

1919
import java.util.UUID;
20+
import java.util.function.Consumer;
2021

2122
import reactor.core.publisher.Mono;
2223
import reactor.core.scheduler.Schedulers;
@@ -36,6 +37,7 @@
3637
* @author Eric Deandrea
3738
* @author Thomas Vitale
3839
* @author Alonso Araya
40+
* @author Alex Montoya
3941
* @since 5.1
4042
*/
4143
public final class CookieServerCsrfTokenRepository implements ServerCsrfTokenRepository {
@@ -60,6 +62,19 @@ public final class CookieServerCsrfTokenRepository implements ServerCsrfTokenRep
6062

6163
private int cookieMaxAge = -1;
6264

65+
private Consumer<ResponseCookie.ResponseCookieBuilder> cookieCustomizer = (builder) -> {};
66+
67+
/**
68+
* Add a {@link Consumer} for a {@code ResponseCookieBuilder} that will be invoked
69+
* for each cookie being built, just before the call to {@code build()}.
70+
* @param cookieCustomizer consumer for a cookie builder
71+
* @since 6.1
72+
*/
73+
public void setCookieCustomizer(Consumer<ResponseCookie.ResponseCookieBuilder> cookieCustomizer) {
74+
Assert.notNull(cookieCustomizer, "cookieCustomizer must not be null");
75+
this.cookieCustomizer = cookieCustomizer;
76+
}
77+
6378
/**
6479
* Factory method to conveniently create an instance that has
6580
* {@link #setCookieHttpOnly(boolean)} set to false.
@@ -82,16 +97,18 @@ public Mono<Void> saveToken(ServerWebExchange exchange, CsrfToken token) {
8297
return Mono.fromRunnable(() -> {
8398
String tokenValue = (token != null) ? token.getToken() : "";
8499
// @formatter:off
85-
ResponseCookie cookie = ResponseCookie
100+
ResponseCookie.ResponseCookieBuilder cookieBuilder = ResponseCookie
86101
.from(this.cookieName, tokenValue)
87102
.domain(this.cookieDomain)
88103
.httpOnly(this.cookieHttpOnly)
89104
.maxAge(!tokenValue.isEmpty() ? this.cookieMaxAge : 0)
90105
.path((this.cookiePath != null) ? this.cookiePath : getRequestContext(exchange.getRequest()))
91-
.secure((this.secure != null) ? this.secure : (exchange.getRequest().getSslInfo() != null))
92-
.build();
106+
.secure((this.secure != null) ? this.secure : (exchange.getRequest().getSslInfo() != null));
107+
108+
this.cookieCustomizer.accept(cookieBuilder);
109+
93110
// @formatter:on
94-
exchange.getResponse().addCookie(cookie);
111+
exchange.getResponse().addCookie(cookieBuilder.build());
95112
});
96113
}
97114

@@ -107,9 +124,9 @@ public Mono<CsrfToken> loadToken(ServerWebExchange exchange) {
107124
}
108125

109126
/**
110-
* Sets the HttpOnly attribute on the cookie containing the CSRF token
111-
* @param cookieHttpOnly True to mark the cookie as http only. False otherwise.
127+
* @deprecated Use {@link #setCookieCustomizer(Consumer)} instead.
112128
*/
129+
@Deprecated(since = "6.1")
113130
public void setCookieHttpOnly(boolean cookieHttpOnly) {
114131
this.cookieHttpOnly = cookieHttpOnly;
115132
}
@@ -150,44 +167,27 @@ public void setCookiePath(String cookiePath) {
150167
}
151168

152169
/**
153-
* Sets the cookie domain
154-
* @param cookieDomain The cookie domain
170+
* @deprecated Use {@link #setCookieCustomizer(Consumer)} instead.
155171
*/
172+
@Deprecated(since = "6.1")
156173
public void setCookieDomain(String cookieDomain) {
157174
this.cookieDomain = cookieDomain;
158175
}
159176

160177
/**
161-
* Sets the cookie secure flag. If not set, the value depends on
162-
* {@link ServerHttpRequest#getSslInfo()}.
163-
* @param secure The value for the secure flag
178+
* @deprecated Use {@link #setCookieCustomizer(Consumer)} instead.
164179
* @since 5.5
165180
*/
181+
@Deprecated(since = "6.1")
166182
public void setSecure(boolean secure) {
167183
this.secure = secure;
168184
}
169185

170186
/**
171-
* Sets maximum age in seconds for the cookie that the expected CSRF token is saved to
172-
* and read from. By default maximum age value is -1.
173-
*
174-
* <p>
175-
* A positive value indicates that the cookie will expire after that many seconds have
176-
* passed. Note that the value is the <i>maximum</i> age when the cookie will expire,
177-
* not the cookie's current age.
178-
*
179-
* <p>
180-
* A negative value means that the cookie is not stored persistently and will be
181-
* deleted when the Web browser exits.
182-
*
183-
* <p>
184-
* A zero value causes the cookie to be deleted immediately therefore it is not a
185-
* valid value and in that case an {@link IllegalArgumentException} will be thrown.
186-
* @param cookieMaxAge an integer specifying the maximum age of the cookie in seconds;
187-
* if negative, means the cookie is not stored; if zero, the method throws an
188-
* {@link IllegalArgumentException}
187+
* @deprecated Use {@link #setCookieCustomizer(Consumer)} instead.
189188
* @since 5.8
190189
*/
190+
@Deprecated(since = "6.1")
191191
public void setCookieMaxAge(int cookieMaxAge) {
192192
Assert.isTrue(cookieMaxAge != 0, "cookieMaxAge cannot be zero");
193193
this.cookieMaxAge = cookieMaxAge;

0 commit comments

Comments
 (0)