简介
出于安全方面考虑,浏览器发起请求时,会先检查同源策略(协议、主机、端口是否与当前页面相同),不匹配则认为是跨域请求。
CORS (Cross-Origin Resource Sharing)
CORS
是一种机制,允许服务器声明哪些域(origin)可以访问其资源,从而绕过同源策略的限制。浏览器会发送预检请求(Preflight Request)以确定是否允许跨域访问。
Preflight请求
Preflight请求是跨域资源共享(CORS)中的一种机制,用于在实际请求之前发送一个预检请求。浏览器在发送某些类型的跨域请求(例如带有自定义标头的请求)之前,会自动发送一个OPTIONS请求,以获取目标服务器是否允许实际请求的权限。
预检请求包含一组查询信息,询问服务器是否允许实际请求。这些查询信息包括:
- Access-Control-Request-Method: 表示实际请求中将使用的 HTTP 方法(例如 GET、POST)。
- Access-Control-Request-Headers: 表示实际请求中将使用的自定义 HTTP 标头。
服务器收到预检请求后,会检查这些信息,然后决定是否允许实际请求。如果服务器允许,它会在响应中包含相应的 CORS 头(例如 Access-Control-Allow-Origin、Access-Control-Allow-Methods 等)。
这个预检请求机制有助于确保安全,因为它防止了潜在的恶意跨域请求。如果服务器支持并验证了预检请求,浏览器才会允许实际请求。
以下是一个预检请求的示例:
请求:
makefile
OPTIONS /example/resource HTTP/1.1
Host: example.com
Access-Control-Request-Method: POST
Access-Control-Request-Headers: Content-Type, Authorization
响应:
makefile
HTTP/1.1 200 OK
Access-Control-Allow-Origin: http://example.com
Access-Control-Allow-Methods: GET, POST
Access-Control-Allow-Headers: Content-Type, Authorization
Spring MVC跨域设置
浏览器发起Preflight请求,SpringMVC的处理流程为:
DispatcherServlet#doDispatch
-> HttpRequestHandlerAdapter#handle
-> AbstractHandlerMapping#handleRequest
-> DefaultCorsProcessor#processRequest
DefaultCorsProcessor
会根据当前配置的跨域规则,检查当前资源你是否允许发起的域访问,检查不通过时直接返回403 Forbidden
,body为Invalid CORS request。
注解方式
可以在类,或者方法上使用@CrossOrigin(origins = "*", methods = {RequestMethod.GET, RequestMethod.POST, RequestMethod.OPTIONS})
Filter模式
对于使用了Spring-Web而没有使用SpringMVC的项目,可以使用Spring提供的CorsFilter
,它会拦截的Servlet请求,并添加一些允许跨域的头,以下是允许所有请求跨域的示例
java
@Configuration
public class CorsConfig {
@Bean
public CorsFilter corsFilter() {
UrlBasedCorsConfigurationSource source = new UrlBasedCorsConfigurationSource();
CorsConfiguration config = new CorsConfiguration();
config.addAllowedOrigin("*");
config.addAllowedMethod("*");
config.addAllowedHeader("*");
source.registerCorsConfiguration("/**", config);
return new CorsFilter(source);
}
}
测试
可以使用以下html进行跨域测试
html
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>POST Request Form</title>
</head>
<body>
<h1>POST Request Form</h1>
<form id="postForm">
<label for="url">URL:</label>
<input type="text" id="url" name="url" value="http://xxx" style="width: 100%; margin-bottom: 10px;">
<label for="postData">POST Data:</label>
<textarea id="postData" name="postData" style="width: 100%; height: 100px; margin-bottom: 10px;">
{
}
</textarea>
<button type="button" onclick="sendPostRequest()">Send POST Request</button>
</form>
<script>
function sendPostRequest() {
var url = document.getElementById("url").value;
var postData = document.getElementById("postData").value;
fetch(url, {
method: 'POST',
headers: {
'Content-Type': 'application/json',
},
body: postData,
})
.then(response => response.json())
.then(data => {
console.log('Success:', data);
alert('POST request sent successfully!');
})
.catch((error) => {
console.error('Error:', error);
alert('Error sending POST request!');
});
}
</script>
</body>
</html>