Skip to content

Commit 5e8d838

Browse files
committed
CORS-related refinements
After this change CorsProcessor has a single processRequest method and it also explicitly deals with a null CorsConfiguration, which for pre-flight requests results in a rejection while for simple requests results in no CORS headers added. The AbstractHandlerMapping now uses a LinkedHashMap to preserve the order in which global patterns are provided.
1 parent 0b84f13 commit 5e8d838

File tree

8 files changed

+227
-194
lines changed

8 files changed

+227
-194
lines changed

spring-web/src/main/java/org/springframework/web/cors/CorsProcessor.java

Lines changed: 15 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -21,35 +21,31 @@
2121
import javax.servlet.http.HttpServletResponse;
2222

2323
/**
24-
* Contract for handling CORS preflight requests and intercepting CORS simple
25-
* and actual requests.
24+
* A strategy that takes a request and a {@link CorsConfiguration} and updates
25+
* the response.
26+
*
27+
* <p>This component is not concerned with how a {@code CorsConfiguration} is
28+
* selected but rather takes follow-up actions such as applying CORS validation
29+
* checks and either rejecting the response or adding CORS headers to the
30+
* response.
2631
*
2732
* @author Sebastien Deleuze
33+
* @author Rossen Stoyanchev
2834
* @since 4.2
2935
* @see <a href="http://www.w3.org/TR/cors/">CORS W3C recommandation</a>
36+
* @see org.springframework.web.servlet.handler.AbstractHandlerMapping#setCorsProcessor
3037
*/
3138
public interface CorsProcessor {
3239

3340
/**
34-
* Process a preflight CORS request given a {@link CorsConfiguration}.
35-
* If the request is not a valid CORS pre-flight request or if it does not
36-
* comply with the configuration it should be rejected.
37-
* If the request is valid and complies with the configuration, CORS headers
38-
* should be added to the response.
39-
* @return {@code false} if the request is rejected, else {@code true}.
40-
*/
41-
boolean processPreFlightRequest(CorsConfiguration conf, HttpServletRequest request,
42-
HttpServletResponse response) throws IOException;
43-
44-
/**
45-
* Process a simple or actual CORS request given a {@link CorsConfiguration}.
46-
* If the request is not a valid CORS simple or actual request or if it does
47-
* not comply with the configuration, it should be rejected.
48-
* If the request is valid and comply with the configuration, this method adds the related
49-
* CORS headers to the response.
41+
* Process a request given a {@code CorsConfiguration}.
42+
*
43+
* @param configuration the applicable CORS configuration, possibly {@code null}
44+
* @param request the current request
45+
* @param response the current response
5046
* @return {@code false} if the request is rejected, else {@code true}.
5147
*/
52-
boolean processActualRequest(CorsConfiguration conf, HttpServletRequest request,
48+
boolean processRequest(CorsConfiguration configuration, HttpServletRequest request,
5349
HttpServletResponse response) throws IOException;
5450

5551
}

spring-web/src/main/java/org/springframework/web/cors/CorsUtils.java

Lines changed: 2 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -34,17 +34,14 @@ public class CorsUtils {
3434
* Returns {@code true} if the request is a valid CORS one.
3535
*/
3636
public static boolean isCorsRequest(HttpServletRequest request) {
37-
return request.getHeader(HttpHeaders.ORIGIN) != null;
37+
return (request.getHeader(HttpHeaders.ORIGIN) != null);
3838
}
3939

4040
/**
4141
* Returns {@code true} if the request is a valid CORS pre-flight one.
4242
*/
4343
public static boolean isPreFlightRequest(HttpServletRequest request) {
44-
if (!isCorsRequest(request)) {
45-
return false;
46-
}
47-
return request.getMethod().equals(HttpMethod.OPTIONS.name());
44+
return (isCorsRequest(request) && request.getMethod().equals(HttpMethod.OPTIONS.name()));
4845
}
4946

5047
}

spring-web/src/main/java/org/springframework/web/cors/DefaultCorsProcessor.java

Lines changed: 43 additions & 46 deletions
Original file line numberDiff line numberDiff line change
@@ -39,7 +39,11 @@
3939

4040
/**
4141
* Default implementation of {@link CorsProcessor}, as defined by the
42-
* <a href="http://www.w3.org/TR/cors/">CORS W3C recommandation</a>.
42+
* <a href="http://www.w3.org/TR/cors/">CORS W3C recommendation</a>.
43+
*
44+
* <p>Note that when input {@link CorsConfiguration} is {@code null}, this
45+
* implementation does not reject simple or actual requests outright but simply
46+
* avoid adding CORS headers to the response.
4347
*
4448
* @author Sebastien Deleuze
4549
* @author Rossen Stoyanhcev
@@ -49,48 +53,37 @@ public class DefaultCorsProcessor implements CorsProcessor {
4953

5054
private static final Charset UTF8_CHARSET = Charset.forName("UTF-8");
5155

52-
53-
protected final Log logger = LogFactory.getLog(getClass());
56+
private static final Log logger = LogFactory.getLog(DefaultCorsProcessor.class);
5457

5558

5659
@Override
57-
public boolean processPreFlightRequest(CorsConfiguration config, HttpServletRequest request,
60+
public boolean processRequest(CorsConfiguration config, HttpServletRequest request,
5861
HttpServletResponse response) throws IOException {
5962

60-
Assert.isTrue(CorsUtils.isPreFlightRequest(request));
61-
62-
ServerHttpResponse serverResponse = new ServletServerHttpResponse(response);
63-
if (responseHasCors(serverResponse)) {
64-
return true;
65-
}
66-
67-
ServerHttpRequest serverRequest = new ServletServerHttpRequest(request);
68-
if (handleInternal(serverRequest, serverResponse, config, true)) {
69-
serverResponse.flush();
63+
if (!CorsUtils.isCorsRequest(request)) {
7064
return true;
7165
}
7266

73-
return false;
74-
}
75-
76-
@Override
77-
public boolean processActualRequest(CorsConfiguration config, HttpServletRequest request,
78-
HttpServletResponse response) throws IOException {
79-
80-
Assert.isTrue(CorsUtils.isCorsRequest(request) && !CorsUtils.isPreFlightRequest(request));
81-
8267
ServletServerHttpResponse serverResponse = new ServletServerHttpResponse(response);
68+
ServletServerHttpRequest serverRequest = new ServletServerHttpRequest(request);
69+
8370
if (responseHasCors(serverResponse)) {
8471
return true;
8572
}
8673

87-
ServletServerHttpRequest serverRequest = new ServletServerHttpRequest(request);
88-
if (handleInternal(serverRequest, serverResponse, config, false)) {
89-
serverResponse.flush();
90-
return true;
74+
boolean preFlightRequest = CorsUtils.isPreFlightRequest(request);
75+
76+
if (config == null) {
77+
if (preFlightRequest) {
78+
rejectRequest(serverResponse);
79+
return false;
80+
}
81+
else {
82+
return true;
83+
}
9184
}
9285

93-
return false;
86+
return handleInternal(serverRequest, serverResponse, config, preFlightRequest);
9487
}
9588

9689
private boolean responseHasCors(ServerHttpResponse response) {
@@ -107,32 +100,45 @@ private boolean responseHasCors(ServerHttpResponse response) {
107100
return hasAllowOrigin;
108101
}
109102

103+
/**
104+
* Invoked when one of the CORS checks failed.
105+
* The default implementation sets the response status to 403 and writes
106+
* "Invalid CORS request" to the response.
107+
*/
108+
protected void rejectRequest(ServerHttpResponse response) throws IOException {
109+
response.setStatusCode(HttpStatus.FORBIDDEN);
110+
response.getBody().write("Invalid CORS request".getBytes(UTF8_CHARSET));
111+
}
112+
113+
/**
114+
* Handle the given request.
115+
*/
110116
protected boolean handleInternal(ServerHttpRequest request, ServerHttpResponse response,
111-
CorsConfiguration config, boolean isPreFlight) throws IOException {
117+
CorsConfiguration config, boolean preFlightRequest) throws IOException {
112118

113119
String requestOrigin = request.getHeaders().getOrigin();
114120
String allowOrigin = checkOrigin(config, requestOrigin);
115121

116-
HttpMethod requestMethod = getMethodToUse(request, isPreFlight);
122+
HttpMethod requestMethod = getMethodToUse(request, preFlightRequest);
117123
List<HttpMethod> allowMethods = checkMethods(config, requestMethod);
118124

119-
List<String> requestHeaders = getHeadersToUse(request, isPreFlight);
125+
List<String> requestHeaders = getHeadersToUse(request, preFlightRequest);
120126
List<String> allowHeaders = checkHeaders(config, requestHeaders);
121127

122-
if (allowOrigin == null || allowMethods == null || (isPreFlight && allowHeaders == null)) {
123-
handleInvalidCorsRequest(response);
128+
if (allowOrigin == null || allowMethods == null || (preFlightRequest && allowHeaders == null)) {
129+
rejectRequest(response);
124130
return false;
125131
}
126132

127133
HttpHeaders responseHeaders = response.getHeaders();
128134
responseHeaders.setAccessControlAllowOrigin(allowOrigin);
129135
responseHeaders.add(HttpHeaders.VARY, HttpHeaders.ORIGIN);
130136

131-
if (isPreFlight) {
137+
if (preFlightRequest) {
132138
responseHeaders.setAccessControlAllowMethods(allowMethods);
133139
}
134140

135-
if (isPreFlight && !allowHeaders.isEmpty()) {
141+
if (preFlightRequest && !allowHeaders.isEmpty()) {
136142
responseHeaders.setAccessControlAllowHeaders(allowHeaders);
137143
}
138144

@@ -144,10 +150,11 @@ protected boolean handleInternal(ServerHttpRequest request, ServerHttpResponse r
144150
responseHeaders.setAccessControlAllowCredentials(true);
145151
}
146152

147-
if (isPreFlight && config.getMaxAge() != null) {
153+
if (preFlightRequest && config.getMaxAge() != null) {
148154
responseHeaders.setAccessControlMaxAge(config.getMaxAge());
149155
}
150156

157+
response.flush();
151158
return true;
152159
}
153160

@@ -187,14 +194,4 @@ private List<String> getHeadersToUse(ServerHttpRequest request, boolean isPreFli
187194
return (isPreFlight ? headers.getAccessControlRequestHeaders() : new ArrayList<String>(headers.keySet()));
188195
}
189196

190-
/**
191-
* Invoked when one of the CORS checks failed.
192-
* The default implementation sets the response status to 403 and writes
193-
* "Invalid CORS request" to the response.
194-
*/
195-
protected void handleInvalidCorsRequest(ServerHttpResponse response) throws IOException {
196-
response.setStatusCode(HttpStatus.FORBIDDEN);
197-
response.getBody().write("Invalid CORS request".getBytes(UTF8_CHARSET));
198-
}
199-
200197
}

0 commit comments

Comments
 (0)