Skip to content

Commit d9399df

Browse files
mchesjzheaux
authored andcommitted
Allow redirect status code to be customized
Closes gh-12797
1 parent 2638555 commit d9399df

File tree

3 files changed

+80
-3
lines changed

3 files changed

+80
-3
lines changed

web/src/main/java/org/springframework/security/web/DefaultRedirectStrategy.java

+28-2
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
/*
2-
* Copyright 2002-2016 the original author or authors.
2+
* Copyright 2002-2023 the original author or authors.
33
*
44
* Licensed under the Apache License, Version 2.0 (the "License");
55
* you may not use this file except in compliance with the License.
@@ -24,6 +24,8 @@
2424
import org.apache.commons.logging.LogFactory;
2525

2626
import org.springframework.core.log.LogMessage;
27+
import org.springframework.http.HttpHeaders;
28+
import org.springframework.http.HttpStatus;
2729
import org.springframework.security.web.util.UrlUtils;
2830
import org.springframework.util.Assert;
2931

@@ -32,6 +34,7 @@
3234
* the framework.
3335
*
3436
* @author Luke Taylor
37+
* @author Mark Chesney
3538
* @since 3.0
3639
*/
3740
public class DefaultRedirectStrategy implements RedirectStrategy {
@@ -40,6 +43,8 @@ public class DefaultRedirectStrategy implements RedirectStrategy {
4043

4144
private boolean contextRelative;
4245

46+
private HttpStatus statusCode = HttpStatus.FOUND;
47+
4348
/**
4449
* Redirects the response to the supplied URL.
4550
* <p>
@@ -55,7 +60,14 @@ public void sendRedirect(HttpServletRequest request, HttpServletResponse respons
5560
if (this.logger.isDebugEnabled()) {
5661
this.logger.debug(LogMessage.format("Redirecting to %s", redirectUrl));
5762
}
58-
response.sendRedirect(redirectUrl);
63+
if (this.statusCode == HttpStatus.FOUND) {
64+
response.sendRedirect(redirectUrl);
65+
}
66+
else {
67+
response.setHeader(HttpHeaders.LOCATION, redirectUrl);
68+
response.setStatus(this.statusCode.value());
69+
response.getWriter().flush();
70+
}
5971
}
6072

6173
protected String calculateRedirectUrl(String contextPath, String url) {
@@ -96,4 +108,18 @@ protected boolean isContextRelative() {
96108
return this.contextRelative;
97109
}
98110

111+
/**
112+
* Sets the HTTP status code to use. The default is {@link HttpStatus#FOUND}.
113+
* <p>
114+
* Note that according to RFC 7231, with {@link HttpStatus#FOUND}, a user agent MAY
115+
* change the request method from POST to GET for the subsequent request. If this
116+
* behavior is undesired, {@link HttpStatus#TEMPORARY_REDIRECT} can be used instead.
117+
* @param statusCode the HTTP status code to use.
118+
* @since 6.2
119+
*/
120+
public void setStatusCode(HttpStatus statusCode) {
121+
Assert.notNull(statusCode, "statusCode cannot be null");
122+
this.statusCode = statusCode;
123+
}
124+
99125
}

web/src/test/java/org/springframework/security/web/DefaultRedirectStrategyTests.java

+20-1
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
/*
2-
* Copyright 2002-2016 the original author or authors.
2+
* Copyright 2002-2023 the original author or authors.
33
*
44
* Licensed under the Apache License, Version 2.0 (the "License");
55
* you may not use this file except in compliance with the License.
@@ -18,6 +18,7 @@
1818

1919
import org.junit.jupiter.api.Test;
2020

21+
import org.springframework.http.HttpStatus;
2122
import org.springframework.mock.web.MockHttpServletRequest;
2223
import org.springframework.mock.web.MockHttpServletResponse;
2324

@@ -26,6 +27,7 @@
2627

2728
/**
2829
* @author Luke Taylor
30+
* @author Mark Chesney
2931
* @since 3.0
3032
*/
3133
public class DefaultRedirectStrategyTests {
@@ -64,4 +66,21 @@ public void contextRelativeShouldThrowExceptionIfURLDoesNotContainContextPath()
6466
.isThrownBy(() -> rds.sendRedirect(request, response, "https://redirectme.somewhere.else"));
6567
}
6668

69+
@Test
70+
public void statusCodeIsHandledCorrectly() throws Exception {
71+
// given
72+
DefaultRedirectStrategy redirectStrategy = new DefaultRedirectStrategy();
73+
redirectStrategy.setStatusCode(HttpStatus.TEMPORARY_REDIRECT);
74+
MockHttpServletRequest request = new MockHttpServletRequest();
75+
MockHttpServletResponse response = new MockHttpServletResponse();
76+
77+
// when
78+
redirectStrategy.sendRedirect(request, response, "/requested");
79+
80+
// then
81+
assertThat(response.isCommitted()).isTrue();
82+
assertThat(response.getRedirectedUrl()).isEqualTo("/requested");
83+
assertThat(response.getStatus()).isEqualTo(307);
84+
}
85+
6786
}

web/src/test/java/org/springframework/security/web/session/SessionManagementFilterTests.java

+32
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,7 @@
2323
import org.junit.jupiter.api.BeforeEach;
2424
import org.junit.jupiter.api.Test;
2525

26+
import org.springframework.http.HttpStatus;
2627
import org.springframework.mock.web.MockFilterChain;
2728
import org.springframework.mock.web.MockHttpServletRequest;
2829
import org.springframework.mock.web.MockHttpServletResponse;
@@ -210,6 +211,37 @@ public void responseIsRedirectedToRequestedUrlIfContextPathIsSetAndSessionIsInva
210211
assertThat(response.getStatus()).isEqualTo(302);
211212
}
212213

214+
@Test
215+
public void responseIsRedirectedToRequestedUrlIfStatusCodeIsSetAndSessionIsInvalid() throws Exception {
216+
// given
217+
DefaultRedirectStrategy redirectStrategy = new DefaultRedirectStrategy();
218+
redirectStrategy.setStatusCode(HttpStatus.TEMPORARY_REDIRECT);
219+
RequestedUrlRedirectInvalidSessionStrategy invalidSessionStrategy = new RequestedUrlRedirectInvalidSessionStrategy();
220+
invalidSessionStrategy.setCreateNewSession(true);
221+
invalidSessionStrategy.setRedirectStrategy(redirectStrategy);
222+
SecurityContextRepository securityContextRepository = mock(SecurityContextRepository.class);
223+
SessionAuthenticationStrategy sessionAuthenticationStrategy = mock(SessionAuthenticationStrategy.class);
224+
SessionManagementFilter filter = new SessionManagementFilter(securityContextRepository,
225+
sessionAuthenticationStrategy);
226+
filter.setInvalidSessionStrategy(invalidSessionStrategy);
227+
MockHttpServletRequest request = new MockHttpServletRequest();
228+
request.setRequestedSessionId("xxx");
229+
request.setRequestedSessionIdValid(false);
230+
request.setRequestURI("/requested");
231+
MockHttpServletResponse response = new MockHttpServletResponse();
232+
FilterChain chain = mock(FilterChain.class);
233+
234+
// when
235+
filter.doFilter(request, response, chain);
236+
237+
// then
238+
verify(securityContextRepository).containsContext(request);
239+
verifyNoMoreInteractions(securityContextRepository, sessionAuthenticationStrategy, chain);
240+
assertThat(response.isCommitted()).isTrue();
241+
assertThat(response.getRedirectedUrl()).isEqualTo("/requested");
242+
assertThat(response.getStatus()).isEqualTo(307);
243+
}
244+
213245
@Test
214246
public void customAuthenticationTrustResolver() throws Exception {
215247
AuthenticationTrustResolver trustResolver = mock(AuthenticationTrustResolver.class);

0 commit comments

Comments
 (0)