Saved requests cache the original request destination so users can be redirected after authentication.
Interface for caching and retrieving saved requests.
package org.springframework.security.web.savedrequest;
import jakarta.servlet.http.HttpServletRequest;
import jakarta.servlet.http.HttpServletResponse;
public interface RequestCache {
/**
* Saves the current request for later retrieval.
*
* @param request the HTTP request
* @param response the HTTP response
*/
void saveRequest(HttpServletRequest request, HttpServletResponse response);
/**
* Returns the saved request.
*
* @param request the current request
* @param response the current response
* @return the saved request or null
*/
SavedRequest getRequest(HttpServletRequest request, HttpServletResponse response);
/**
* Returns a request that matches the saved request.
*
* @param request the current request
* @param response the current response
* @return the matching request or null
*/
HttpServletRequest getMatchingRequest(HttpServletRequest request, HttpServletResponse response);
/**
* Removes the saved request.
*
* @param request the current request
* @param response the current response
*/
void removeRequest(HttpServletRequest request, HttpServletResponse response);
}Stores saved requests in the HTTP session.
package org.springframework.security.web.savedrequest;
import jakarta.servlet.http.HttpServletRequest;
import jakarta.servlet.http.HttpServletResponse;
import org.springframework.security.web.util.matcher.RequestMatcher;
public class HttpSessionRequestCache implements RequestCache {
public static final String SAVED_REQUEST = "SPRING_SECURITY_SAVED_REQUEST";
public HttpSessionRequestCache();
public void saveRequest(HttpServletRequest request, HttpServletResponse response);
public SavedRequest getRequest(HttpServletRequest request, HttpServletResponse response);
public HttpServletRequest getMatchingRequest(HttpServletRequest request, HttpServletResponse response);
public void removeRequest(HttpServletRequest request, HttpServletResponse response);
/**
* Sets matcher for requests that should be cached.
* Default: caches non-GET requests only.
*/
public void setRequestMatcher(RequestMatcher requestMatcher);
/**
* Sets the session attribute name (default: SPRING_SECURITY_SAVED_REQUEST).
*/
public void setSessionAttrName(String sessionAttrName);
/**
* Sets whether to allow session creation (default: true).
*/
public void setCreateSessionAllowed(boolean createSessionAllowed);
/**
* Sets the port resolver for scheme-relative URLs.
*/
public void setPortResolver(PortResolver portResolver);
}A RequestCache that does not save requests (for stateless applications).
package org.springframework.security.web.savedrequest;
import jakarta.servlet.http.HttpServletRequest;
import jakarta.servlet.http.HttpServletResponse;
public class NullRequestCache implements RequestCache {
public NullRequestCache();
public void saveRequest(HttpServletRequest request, HttpServletResponse response);
public SavedRequest getRequest(HttpServletRequest request, HttpServletResponse response);
public HttpServletRequest getMatchingRequest(HttpServletRequest request, HttpServletResponse response);
public void removeRequest(HttpServletRequest request, HttpServletResponse response);
}Stores saved requests in a cookie instead of the session.
package org.springframework.security.web.savedrequest;
import jakarta.servlet.http.HttpServletRequest;
import jakarta.servlet.http.HttpServletResponse;
public class CookieRequestCache implements RequestCache {
/**
* Creates a cookie-based request cache.
*/
public CookieRequestCache();
public void saveRequest(HttpServletRequest request, HttpServletResponse response);
public SavedRequest getRequest(HttpServletRequest request, HttpServletResponse response);
public HttpServletRequest getMatchingRequest(HttpServletRequest request, HttpServletResponse response);
public void removeRequest(HttpServletRequest request, HttpServletResponse response);
/**
* Sets the matcher for requests that should be cached.
*/
public void setRequestMatcher(RequestMatcher requestMatcher);
/**
* Sets the cookie name for storing the saved request.
* Default: "REDIRECT_URI"
*/
public void setCookieName(String cookieName);
/**
* Sets the cookie max age in seconds.
* Default: -1 (session cookie)
*/
public void setCookieMaxAge(int cookieMaxAge);
/**
* Sets whether the cookie should be secure (HTTPS only).
*/
public void setCookieSecure(boolean cookieSecure);
/**
* Sets whether the cookie should be HTTP-only.
* Default: true
*/
public void setCookieHttpOnly(boolean cookieHttpOnly);
/**
* Sets the cookie domain.
*/
public void setCookieDomain(String cookieDomain);
/**
* Sets the cookie path.
* Default: "/"
*/
public void setCookiePath(String cookiePath);
}Represents a cached HTTP request.
package org.springframework.security.web.savedrequest;
import jakarta.servlet.http.Cookie;
import java.util.Collection;
import java.util.List;
import java.util.Locale;
import java.util.Map;
public interface SavedRequest {
/**
* Returns the URL to redirect to.
*/
String getRedirectUrl();
/**
* Returns the cookies from the saved request.
*/
List<Cookie> getCookies();
/**
* Returns the HTTP method.
*/
String getMethod();
/**
* Returns header values for the given name.
*/
List<String> getHeaderValues(String name);
/**
* Returns all header names.
*/
Collection<String> getHeaderNames();
/**
* Returns the locales from the request.
*/
List<Locale> getLocales();
/**
* Returns parameter values for the given name.
*/
String[] getParameterValues(String name);
/**
* Returns all parameters.
*/
Map<String, String[]> getParameterMap();
}Default implementation of SavedRequest.
package org.springframework.security.web.savedrequest;
import jakarta.servlet.http.Cookie;
import jakarta.servlet.http.HttpServletRequest;
import java.util.Collection;
import java.util.List;
import java.util.Locale;
import java.util.Map;
public class DefaultSavedRequest implements SavedRequest {
/**
* Creates a saved request from the HttpServletRequest.
*/
public DefaultSavedRequest(HttpServletRequest request, PortResolver portResolver);
public String getRedirectUrl();
public List<Cookie> getCookies();
public String getMethod();
public List<String> getHeaderValues(String name);
public Collection<String> getHeaderNames();
public List<Locale> getLocales();
public String[] getParameterValues(String name);
public Map<String, String[]> getParameterMap();
public String getContextPath();
public String getQueryString();
public String getRequestURI();
public String getRequestURL();
public String getScheme();
public String getServerName();
public int getServerPort();
public String getServletPath();
}Simplified implementation of SavedRequest (Since 4.0).
package org.springframework.security.web.savedrequest;
import java.util.Collection;
import java.util.List;
import java.util.Locale;
import java.util.Map;
public class SimpleSavedRequest implements SavedRequest {
/**
* Creates a simple saved request.
*
* @param redirectUrl the URL to redirect to
* @since 4.0
*/
public SimpleSavedRequest(String redirectUrl);
/**
* Creates a simple saved request from another saved request.
*
* @param request the saved request to copy from
* @since 4.0
*/
public SimpleSavedRequest(SavedRequest request);
public String getRedirectUrl();
public List<SavedCookie> getCookies();
public String getMethod();
public List<String> getHeaderValues(String name);
public Collection<String> getHeaderNames();
public List<Locale> getLocales();
public String[] getParameterValues(String name);
public Map<String, String[]> getParameterMap();
}Represents a saved HTTP cookie.
package org.springframework.security.web.savedrequest;
import jakarta.servlet.http.Cookie;
public class SavedCookie {
/**
* Creates a saved cookie from an HTTP cookie.
*
* @param cookie the HTTP cookie
*/
public SavedCookie(Cookie cookie);
/**
* Creates a saved cookie with the given parameters.
*
* @param name the cookie name
* @param value the cookie value
* @param comment the cookie comment
* @param domain the cookie domain
* @param maxAge the cookie max age
* @param path the cookie path
* @param secure whether the cookie is secure
* @param version the cookie version
*/
public SavedCookie(String name, String value, String comment, String domain,
int maxAge, String path, boolean secure, int version);
public String getName();
public String getValue();
public String getComment();
public String getDomain();
public int getMaxAge();
public String getPath();
public boolean isSecure();
public int getVersion();
/**
* Converts to a Jakarta Servlet Cookie.
*
* @return the cookie
*/
public Cookie getCookie();
}Filter that replaces the request with a saved request if one exists.
package org.springframework.security.web.savedrequest;
import jakarta.servlet.FilterChain;
import jakarta.servlet.ServletException;
import jakarta.servlet.ServletRequest;
import jakarta.servlet.ServletResponse;
import java.io.IOException;
import org.springframework.web.filter.GenericFilterBean;
public class RequestCacheAwareFilter extends GenericFilterBean {
/**
* Creates a filter with the given request cache.
*/
public RequestCacheAwareFilter(RequestCache requestCache);
/**
* Creates a filter with default HttpSessionRequestCache.
*/
public RequestCacheAwareFilter();
public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain)
throws IOException, ServletException;
}import org.springframework.context.annotation.Bean;
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
import org.springframework.security.web.SecurityFilterChain;
import org.springframework.security.web.savedrequest.HttpSessionRequestCache;
import org.springframework.security.web.savedrequest.RequestCache;
@Configuration
public class RequestCacheConfig {
@Bean
public SecurityFilterChain filterChain(HttpSecurity http, RequestCache requestCache)
throws Exception {
http
.requestCache(cache -> cache
.requestCache(requestCache)
)
.formLogin(form -> form
.loginPage("/login")
.permitAll()
);
return http.build();
}
@Bean
public RequestCache requestCache() {
HttpSessionRequestCache cache = new HttpSessionRequestCache();
cache.setCreateSessionAllowed(true);
return cache;
}
}import org.springframework.security.web.savedrequest.HttpSessionRequestCache;
import org.springframework.security.web.savedrequest.RequestCache;
import org.springframework.security.web.servlet.util.matcher.PathPatternRequestMatcher;
import org.springframework.security.web.util.matcher.NegatedRequestMatcher;
@Bean
public RequestCache requestCache() {
HttpSessionRequestCache cache = new HttpSessionRequestCache();
// Only cache requests to protected resources, not public ones
cache.setRequestMatcher(
new NegatedRequestMatcher(
PathPatternRequestMatcher.pathPattern("/public/**")
)
);
return cache;
}import org.springframework.context.annotation.Bean;
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
import org.springframework.security.config.http.SessionCreationPolicy;
import org.springframework.security.web.SecurityFilterChain;
import org.springframework.security.web.savedrequest.NullRequestCache;
@Configuration
public class StatelessConfig {
@Bean
public SecurityFilterChain filterChain(HttpSecurity http) throws Exception {
http
.sessionManagement(session -> session
.sessionCreationPolicy(SessionCreationPolicy.STATELESS)
)
.requestCache(cache -> cache
.requestCache(new NullRequestCache())
);
return http.build();
}
}