Add deferred CsrfTokenRepository.loadDeferredToken
* Move DeferredCsrfToken to top-level and implement Supplier<CsrfToken> * Move RepositoryDeferredCsrfToken to top-level and make package-private * Add CsrfTokenRepository.loadToken(HttpServletRequest, HttpServletResponse) * Update CsrfFilter * Rename CsrfTokenRepositoryRequestHandler to CsrfTokenRequestAttributeHandler Issue gh-11892 Closes gh-11918
This commit is contained in:
@@ -39,17 +39,17 @@ public final class CsrfAuthenticationStrategy implements SessionAuthenticationSt
|
||||
|
||||
private final Log logger = LogFactory.getLog(getClass());
|
||||
|
||||
private final CsrfTokenRepository csrfTokenRepository;
|
||||
private final CsrfTokenRepository tokenRepository;
|
||||
|
||||
private CsrfTokenRequestHandler requestHandler;
|
||||
private CsrfTokenRequestHandler requestHandler = new CsrfTokenRequestAttributeHandler();
|
||||
|
||||
/**
|
||||
* Creates a new instance
|
||||
* @param csrfTokenRepository the {@link CsrfTokenRepository} to use
|
||||
* @param tokenRepository the {@link CsrfTokenRepository} to use
|
||||
*/
|
||||
public CsrfAuthenticationStrategy(CsrfTokenRepository csrfTokenRepository) {
|
||||
this.requestHandler = new CsrfTokenRepositoryRequestHandler(csrfTokenRepository);
|
||||
this.csrfTokenRepository = csrfTokenRepository;
|
||||
public CsrfAuthenticationStrategy(CsrfTokenRepository tokenRepository) {
|
||||
Assert.notNull(tokenRepository, "tokenRepository cannot be null");
|
||||
this.tokenRepository = tokenRepository;
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -65,8 +65,9 @@ public final class CsrfAuthenticationStrategy implements SessionAuthenticationSt
|
||||
@Override
|
||||
public void onAuthentication(Authentication authentication, HttpServletRequest request,
|
||||
HttpServletResponse response) throws SessionAuthenticationException {
|
||||
this.csrfTokenRepository.saveToken(null, request, response);
|
||||
this.requestHandler.handle(request, response);
|
||||
this.tokenRepository.saveToken(null, request, response);
|
||||
DeferredCsrfToken deferredCsrfToken = this.tokenRepository.loadDeferredToken(request, response);
|
||||
this.requestHandler.handle(request, response, deferredCsrfToken::get);
|
||||
this.logger.debug("Replaced CSRF Token");
|
||||
}
|
||||
|
||||
|
||||
@@ -82,30 +82,21 @@ public final class CsrfFilter extends OncePerRequestFilter {
|
||||
|
||||
private final Log logger = LogFactory.getLog(getClass());
|
||||
|
||||
private final CsrfTokenRequestHandler requestHandler;
|
||||
private final CsrfTokenRepository tokenRepository;
|
||||
|
||||
private RequestMatcher requireCsrfProtectionMatcher = DEFAULT_CSRF_MATCHER;
|
||||
|
||||
private AccessDeniedHandler accessDeniedHandler = new AccessDeniedHandlerImpl();
|
||||
|
||||
/**
|
||||
* Creates a new instance.
|
||||
* @param csrfTokenRepository the {@link CsrfTokenRepository} to use
|
||||
* @deprecated Use {@link CsrfFilter#CsrfFilter(CsrfTokenRequestHandler)} instead
|
||||
*/
|
||||
@Deprecated
|
||||
public CsrfFilter(CsrfTokenRepository csrfTokenRepository) {
|
||||
this(new CsrfTokenRepositoryRequestHandler(csrfTokenRepository));
|
||||
}
|
||||
private CsrfTokenRequestHandler requestHandler = new CsrfTokenRequestAttributeHandler();
|
||||
|
||||
/**
|
||||
* Creates a new instance.
|
||||
* @param requestHandler the {@link CsrfTokenRequestHandler} to use. Default is
|
||||
* {@link CsrfTokenRepositoryRequestHandler}.
|
||||
* @param tokenRepository the {@link CsrfTokenRepository} to use
|
||||
*/
|
||||
public CsrfFilter(CsrfTokenRequestHandler requestHandler) {
|
||||
Assert.notNull(requestHandler, "requestHandler cannot be null");
|
||||
this.requestHandler = requestHandler;
|
||||
public CsrfFilter(CsrfTokenRepository tokenRepository) {
|
||||
Assert.notNull(tokenRepository, "tokenRepository cannot be null");
|
||||
this.tokenRepository = tokenRepository;
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -116,7 +107,8 @@ public final class CsrfFilter extends OncePerRequestFilter {
|
||||
@Override
|
||||
protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain filterChain)
|
||||
throws ServletException, IOException {
|
||||
DeferredCsrfToken deferredCsrfToken = this.requestHandler.handle(request, response);
|
||||
DeferredCsrfToken deferredCsrfToken = this.tokenRepository.loadDeferredToken(request, response);
|
||||
this.requestHandler.handle(request, response, deferredCsrfToken::get);
|
||||
if (!this.requireCsrfProtectionMatcher.matches(request)) {
|
||||
if (this.logger.isTraceEnabled()) {
|
||||
this.logger.trace("Did not protect against CSRF since request did not match "
|
||||
@@ -174,6 +166,21 @@ public final class CsrfFilter extends OncePerRequestFilter {
|
||||
this.accessDeniedHandler = accessDeniedHandler;
|
||||
}
|
||||
|
||||
/**
|
||||
* Specifies a {@link CsrfTokenRequestHandler} that is used to make the
|
||||
* {@link CsrfToken} available as a request attribute.
|
||||
*
|
||||
* <p>
|
||||
* The default is {@link CsrfTokenRequestAttributeHandler}.
|
||||
* </p>
|
||||
* @param requestHandler the {@link CsrfTokenRequestHandler} to use
|
||||
* @since 5.8
|
||||
*/
|
||||
public void setRequestHandler(CsrfTokenRequestHandler requestHandler) {
|
||||
Assert.notNull(requestHandler, "requestHandler cannot be null");
|
||||
this.requestHandler = requestHandler;
|
||||
}
|
||||
|
||||
/**
|
||||
* Constant time comparison to prevent against timing attacks.
|
||||
* @param expected
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2002-2013 the original author or authors.
|
||||
* Copyright 2002-2022 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
@@ -26,6 +26,7 @@ import javax.servlet.http.HttpSession;
|
||||
* {@link HttpSession}.
|
||||
*
|
||||
* @author Rob Winch
|
||||
* @author Steve Riesenberg
|
||||
* @since 3.2
|
||||
* @see HttpSessionCsrfTokenRepository
|
||||
*/
|
||||
@@ -55,4 +56,20 @@ public interface CsrfTokenRepository {
|
||||
*/
|
||||
CsrfToken loadToken(HttpServletRequest request);
|
||||
|
||||
/**
|
||||
* Defers loading the {@link CsrfToken} using the {@link HttpServletRequest} and
|
||||
* {@link HttpServletResponse} until it is needed by the application.
|
||||
* <p>
|
||||
* The returned {@link DeferredCsrfToken} is cached to allow subsequent calls to
|
||||
* {@link DeferredCsrfToken#get()} to return the same {@link CsrfToken} without the
|
||||
* cost of loading or generating the token again.
|
||||
* @param request the {@link HttpServletRequest} to use
|
||||
* @param response the {@link HttpServletResponse} to use
|
||||
* @return a {@link DeferredCsrfToken} that will load the {@link CsrfToken}
|
||||
* @since 5.8
|
||||
*/
|
||||
default DeferredCsrfToken loadDeferredToken(HttpServletRequest request, HttpServletResponse response) {
|
||||
return new RepositoryDeferredCsrfToken(this, request, response);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -31,29 +31,10 @@ import org.springframework.util.Assert;
|
||||
* @author Steve Riesenberg
|
||||
* @since 5.8
|
||||
*/
|
||||
public class CsrfTokenRepositoryRequestHandler implements CsrfTokenRequestHandler {
|
||||
|
||||
private final CsrfTokenRepository csrfTokenRepository;
|
||||
public class CsrfTokenRequestAttributeHandler implements CsrfTokenRequestHandler {
|
||||
|
||||
private String csrfRequestAttributeName;
|
||||
|
||||
/**
|
||||
* Creates a new instance.
|
||||
*/
|
||||
public CsrfTokenRepositoryRequestHandler() {
|
||||
this(new HttpSessionCsrfTokenRepository());
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a new instance.
|
||||
* @param csrfTokenRepository the {@link CsrfTokenRepository} to use. Default
|
||||
* {@link HttpSessionCsrfTokenRepository}
|
||||
*/
|
||||
public CsrfTokenRepositoryRequestHandler(CsrfTokenRepository csrfTokenRepository) {
|
||||
Assert.notNull(csrfTokenRepository, "csrfTokenRepository cannot be null");
|
||||
this.csrfTokenRepository = csrfTokenRepository;
|
||||
}
|
||||
|
||||
/**
|
||||
* The {@link CsrfToken} is available as a request attribute named
|
||||
* {@code CsrfToken.class.getName()}. By default, an additional request attribute that
|
||||
@@ -67,18 +48,18 @@ public class CsrfTokenRepositoryRequestHandler implements CsrfTokenRequestHandle
|
||||
}
|
||||
|
||||
@Override
|
||||
public DeferredCsrfToken handle(HttpServletRequest request, HttpServletResponse response) {
|
||||
public void handle(HttpServletRequest request, HttpServletResponse response,
|
||||
Supplier<CsrfToken> deferredCsrfToken) {
|
||||
Assert.notNull(request, "request cannot be null");
|
||||
Assert.notNull(response, "response cannot be null");
|
||||
Assert.notNull(deferredCsrfToken, "deferredCsrfToken cannot be null");
|
||||
|
||||
request.setAttribute(HttpServletResponse.class.getName(), response);
|
||||
DeferredCsrfToken deferredCsrfToken = new RepositoryDeferredCsrfToken(request, response);
|
||||
CsrfToken csrfToken = new SupplierCsrfToken(deferredCsrfToken::get);
|
||||
CsrfToken csrfToken = new SupplierCsrfToken(deferredCsrfToken);
|
||||
request.setAttribute(CsrfToken.class.getName(), csrfToken);
|
||||
String csrfAttrName = (this.csrfRequestAttributeName != null) ? this.csrfRequestAttributeName
|
||||
: csrfToken.getParameterName();
|
||||
request.setAttribute(csrfAttrName, csrfToken);
|
||||
return deferredCsrfToken;
|
||||
}
|
||||
|
||||
private static final class SupplierCsrfToken implements CsrfToken {
|
||||
@@ -114,46 +95,4 @@ public class CsrfTokenRepositoryRequestHandler implements CsrfTokenRequestHandle
|
||||
|
||||
}
|
||||
|
||||
private final class RepositoryDeferredCsrfToken implements DeferredCsrfToken {
|
||||
|
||||
private final HttpServletRequest request;
|
||||
|
||||
private final HttpServletResponse response;
|
||||
|
||||
private CsrfToken csrfToken;
|
||||
|
||||
private Boolean missingToken;
|
||||
|
||||
RepositoryDeferredCsrfToken(HttpServletRequest request, HttpServletResponse response) {
|
||||
this.request = request;
|
||||
this.response = response;
|
||||
}
|
||||
|
||||
@Override
|
||||
public CsrfToken get() {
|
||||
init();
|
||||
return this.csrfToken;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isGenerated() {
|
||||
init();
|
||||
return this.missingToken;
|
||||
}
|
||||
|
||||
private void init() {
|
||||
if (this.csrfToken != null) {
|
||||
return;
|
||||
}
|
||||
this.csrfToken = CsrfTokenRepositoryRequestHandler.this.csrfTokenRepository.loadToken(this.request);
|
||||
this.missingToken = (this.csrfToken == null);
|
||||
if (this.missingToken) {
|
||||
this.csrfToken = CsrfTokenRepositoryRequestHandler.this.csrfTokenRepository.generateToken(this.request);
|
||||
CsrfTokenRepositoryRequestHandler.this.csrfTokenRepository.saveToken(this.csrfToken, this.request,
|
||||
this.response);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
@@ -16,20 +16,22 @@
|
||||
|
||||
package org.springframework.security.web.csrf;
|
||||
|
||||
import java.util.function.Supplier;
|
||||
|
||||
import javax.servlet.http.HttpServletRequest;
|
||||
import javax.servlet.http.HttpServletResponse;
|
||||
|
||||
import org.springframework.util.Assert;
|
||||
|
||||
/**
|
||||
* An interface that is used to determine the {@link CsrfToken} to use and make the
|
||||
* {@link CsrfToken} available as a request attribute. Implementations of this interface
|
||||
* may choose to perform additional tasks or customize how the token is made available to
|
||||
* the application through request attributes.
|
||||
* A callback interface that is used to make the {@link CsrfToken} created by the
|
||||
* {@link CsrfTokenRepository} available as a request attribute. Implementations of this
|
||||
* interface may choose to perform additional tasks or customize how the token is made
|
||||
* available to the application through request attributes.
|
||||
*
|
||||
* @author Steve Riesenberg
|
||||
* @since 5.8
|
||||
* @see CsrfTokenRepositoryRequestHandler
|
||||
* @see CsrfTokenRequestAttributeHandler
|
||||
*/
|
||||
@FunctionalInterface
|
||||
public interface CsrfTokenRequestHandler extends CsrfTokenRequestResolver {
|
||||
@@ -38,8 +40,9 @@ public interface CsrfTokenRequestHandler extends CsrfTokenRequestResolver {
|
||||
* Handles a request using a {@link CsrfToken}.
|
||||
* @param request the {@code HttpServletRequest} being handled
|
||||
* @param response the {@code HttpServletResponse} being handled
|
||||
* @param csrfToken the {@link CsrfToken} created by the {@link CsrfTokenRepository}
|
||||
*/
|
||||
DeferredCsrfToken handle(HttpServletRequest request, HttpServletResponse response);
|
||||
void handle(HttpServletRequest request, HttpServletResponse response, Supplier<CsrfToken> csrfToken);
|
||||
|
||||
@Override
|
||||
default String resolveCsrfTokenValue(HttpServletRequest request, CsrfToken csrfToken) {
|
||||
|
||||
@@ -25,7 +25,7 @@ import javax.servlet.http.HttpServletRequest;
|
||||
*
|
||||
* @author Steve Riesenberg
|
||||
* @since 5.8
|
||||
* @see CsrfTokenRepositoryRequestHandler
|
||||
* @see CsrfTokenRequestAttributeHandler
|
||||
*/
|
||||
@FunctionalInterface
|
||||
public interface CsrfTokenRequestResolver {
|
||||
|
||||
@@ -20,11 +20,12 @@ package org.springframework.security.web.csrf;
|
||||
* An interface that allows delayed access to a {@link CsrfToken} that may be generated.
|
||||
*
|
||||
* @author Rob Winch
|
||||
* @author Steve Riesenberg
|
||||
* @since 5.8
|
||||
*/
|
||||
public interface DeferredCsrfToken {
|
||||
|
||||
/***
|
||||
/**
|
||||
* Gets the {@link CsrfToken}
|
||||
* @return a non-null {@link CsrfToken}
|
||||
*/
|
||||
|
||||
@@ -27,8 +27,9 @@ import org.springframework.util.Assert;
|
||||
*
|
||||
* @author Rob Winch
|
||||
* @since 4.1
|
||||
* @deprecated Use org.springframework.security.web.csrf.CsrfTokenRequestHandler which
|
||||
* returns a {@link DeferredCsrfToken}
|
||||
* @deprecated Use
|
||||
* {@link CsrfTokenRepository#loadDeferredToken(HttpServletRequest, HttpServletResponse)}
|
||||
* which returns a {@link DeferredCsrfToken}
|
||||
*/
|
||||
@Deprecated
|
||||
public final class LazyCsrfTokenRepository implements CsrfTokenRepository {
|
||||
|
||||
@@ -0,0 +1,71 @@
|
||||
/*
|
||||
* Copyright 2002-2022 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* https://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package org.springframework.security.web.csrf;
|
||||
|
||||
import javax.servlet.http.HttpServletRequest;
|
||||
import javax.servlet.http.HttpServletResponse;
|
||||
|
||||
/**
|
||||
* @author Rob Winch
|
||||
* @author Steve Riesenberg
|
||||
* @since 5.8
|
||||
*/
|
||||
final class RepositoryDeferredCsrfToken implements DeferredCsrfToken {
|
||||
|
||||
private final CsrfTokenRepository csrfTokenRepository;
|
||||
|
||||
private final HttpServletRequest request;
|
||||
|
||||
private final HttpServletResponse response;
|
||||
|
||||
private CsrfToken csrfToken;
|
||||
|
||||
private boolean missingToken;
|
||||
|
||||
RepositoryDeferredCsrfToken(CsrfTokenRepository csrfTokenRepository, HttpServletRequest request,
|
||||
HttpServletResponse response) {
|
||||
this.csrfTokenRepository = csrfTokenRepository;
|
||||
this.request = request;
|
||||
this.response = response;
|
||||
}
|
||||
|
||||
@Override
|
||||
public CsrfToken get() {
|
||||
init();
|
||||
return this.csrfToken;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isGenerated() {
|
||||
init();
|
||||
return this.missingToken;
|
||||
}
|
||||
|
||||
private void init() {
|
||||
if (this.csrfToken != null) {
|
||||
return;
|
||||
}
|
||||
|
||||
this.csrfToken = this.csrfTokenRepository.loadToken(this.request);
|
||||
this.missingToken = (this.csrfToken == null);
|
||||
if (this.missingToken) {
|
||||
this.csrfToken = this.csrfTokenRepository.generateToken(this.request);
|
||||
this.csrfTokenRepository.saveToken(this.csrfToken, this.request, this.response);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
Reference in New Issue
Block a user