Http协议访问资源需要符合同源策略,同源策略[same origin policy]是浏览器的一个安全功能, 同源策略是浏览器安全的基石。同源就是必须访问的域名与端口号完全相同。例如下面例子
http://test.com与http?/test.com:81不同源
http://test.com与http?/a.test.com不同源
http://test.com/a.html与http://test.com/b.html同源
互联网应用系统需要高可用和高并发,架构设计需要将也分解到不同的域名,再通过Nginx代理统一起来,这样不可避免地处理不同源间系统访问。为了解决这个问题,提出了跨源资源共享,即 CORS(Cross-Origin Resource Sharing)。
CORS实现原理
传统的Http协议头信息包括:
Accept
Accept-Language
Content-Language
Last-Event-ID
Content-Type
CORS扩展了头信息:
Origin:
Access-Control-Allow-Origin:
Access-Control-Allow-Methods:
Access-Control-Allow-Headers:
Access-Control-Allow-Credentials:
Access-Control-Max-Age:
CORS跨域访问有两种访问方式,一是直接访问,在HTTP请求头上添加Origin字段(记录本域名),如果访问的服务器允许此域名CORS访问,在响应头上添加如下字段
Access-Control-Allow-Origin: http://mydomain1.com
Access-Control-Allow-Methods: GET, POST, PUT, DELETE
Access-Control-Allow-Headers: X-Custom-Header
Access-Control-Allow-Credentials: true
Access-Control-Max-Age: 1728000
如果不允许此域名CORS访问,不需要添加上述响应头信息。
二是间接范围,首先发生一个OPTION请求询问服务器端是否运行本域名跨域访问,如果允许访问返回响应头信息
Access-Control-Allow-Origin: http://mydomain1.com
Access-Control-Allow-Methods: GET, POST, PUT, DELETE
Access-Control-Allow-Headers: X-Custom-Header
Access-Control-Allow-Credentials: true
Access-Control-Max-Age: 1728000
测试不同源Ajax访问结果
mydomain1.com域名下的页面使用Ajax访问mydomain2.com域名下的服务,系统提示错误
mydomain1.com的JSP
<%@page language="java" contentType="text/html; charset=UTF-8"%>
<html>
<head>
<script src="http://libs.baidu.com/jquery/2.1.1/jquery.min.js"></script>
<script>
$(document).ready(function(){
$.ajax({
type: "get",
async: false,
url: "http://mydomain2.com/myjsonp.jsp?city=BeiJing",
dataType: "jsonp",
jsonp: "callback",//获取jsonp回调函数名的参数名
jsonpCallback:"showName2",//自定义的jsonp回调函数名称,必须非空,默认为jQuery自动生成的随机函数名
success: function(json){
var len = json.colleges.length;
var str = '';
for(var i=0;i<len;i++)
str = str + json.colleges[i] +',';
alert('名称=' + json.name + ' 国家=' + json.country + ' 大学='+str);
},
error: function(){
alert('fail');
}
});
});
function doclick()
{
$.ajax({
type: "get",
async: false,
url: "http://mydomain2.com/test",
success: function(data){
for(var i=0;i<data.length;i++)
{
$('#testdiv').append('<li>'+data[i]+'</li>');
}
},
error: function(){
alert('fail');
}
});
}
</script>
</head>
<body>
<h2>Hello World!</h2>
<input type="button" value="Test Cors" onclick="doclick()"/>
<div id="testdiv"></div>
</body>
</html>
mydomain2.com下的Controller
package com.gf;
import java.util.ArrayList;
import java.util.List;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.CrossOrigin;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.ResponseBody;
@Controller
public class TestCtrl {
@RequestMapping("/test")
@ResponseBody
public List<String> test()
{
List<String> lst = new ArrayList<String>();
lst.add("Java");
lst.add("Python");
lst.add("NodeJS");
return lst;
}
}
范围结果,出现跨域错误
SpringBoot服务端添加跨域访问支持
package com.gf;
import java.util.ArrayList;
import java.util.List;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.CrossOrigin;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.ResponseBody;
@Controller
public class TestCtrl {
@CrossOrigin(origins = {"http://mydomain1.com", "null"})
@RequestMapping("/test")
@ResponseBody
public List<String> test()
{
List<String> lst = new ArrayList<String>();
lst.add("Java");
lst.add("Python");
lst.add("NodeJS");
return lst;
}
}
结果
为Controller所有方法添加跨域访问支持
package com.gf;
import java.util.ArrayList;
import java.util.List;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.CrossOrigin;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.ResponseBody;
@CrossOrigin(origins = {"http://mydomain1.com", "null"})
@Controller
public class TestCtrl {
@RequestMapping("/test")
@ResponseBody
public List<String> test()
{
List<String> lst = new ArrayList<String>();
lst.add("Java");
lst.add("Python");
lst.add("NodeJS");
return lst;
}
}
为全部Controller设置跨域支持
package com.gf;
import org.springframework.context.annotation.Configuration;
import org.springframework.web.servlet.config.annotation.CorsRegistry;
import org.springframework.web.servlet.config.annotation.WebMvcConfigurerAdapter;
@Configuration
public class WebMvcConfig extends WebMvcConfigurerAdapter {
@Override
public void addCorsMappings(CorsRegistry registry) {
registry.addMapping("/**")
.allowedOrigins("*")
.allowedMethods("POST", "GET", "PUT", "OPTIONS", "DELETE")
.maxAge(3600)
.allowCredentials(true);
}
}