一、 前言
二、 版本
编号 | 修改日期 | 版本号 | 修改人 | 修改说明 |
---|---|---|---|---|
1 | 2017.09.24 | 1.0 | ZZ | 初稿 |
1 | 2017.12.26 | 1.0 | ZZ | 完善 CORS + Demo |
三、为什么会产生跨域问题
域名、协议、端口者都相同就是同一个域,否则就是跨域。
JavaScript出于安全方面的考虑,采取了同源策略,即当前域的客户端脚本不允许在没有明确授权的情况下,读取其他域的资源。如果没有同源策略,那么一旦误执行了恶意网站的js脚本,该js脚本便可以随意获取其他域页面document、session、cookie等信息,很不安全。
然而我们经常把网站的一些脚本、图片或其他资源放到静态服务器,页面可以更快的加载,而且减少了Web服务器的压力。或者系统间接口调用。这时就产生了跨域问题。
- eg:console 报错如下(端口不同):
XMLHttpRequest cannot load http://localhost:8888/other/index.jsp. No 'Access-Control-Allow-Origin' header is present on the requested resource. Origin 'http://localhost:8080' is therefore not allowed access.
四、如何解决跨域问题
4.1、代理服务器
一种简单的办法,就是把跨域的工作交给服务器,从后台获取其他站点的数据再返回给前台,也就是跨域代理(Cross Domain Proxy)。这种方法似乎蛮简单的,改动也不太大。不过就是http请求多了些,响应慢了些,服务器的负载重了些。
4.2、通过jQuery实现JSONP的方式实现跨域
JSONP 是 JSON with padding(填充式 JSON 或参数式 JSON)的简写。
JSONP实现跨域请求的原理是动态创建
<html>
<body>
<div>
<button id="btn_jsonp" onclick="jsonp_function()">JSONP点击</button>
</div>
</body>
<script type = "text/javascript">
function jsonp_function(){
// $("#btn").click(function() {
$.ajax({
async: true,
url: "http://localhost:8080/ApiDemo_Yshow/Student/getStudentList",
type: "GET",
dataType : "jsonp", // 返回的数据类型,设置为JSONP方式
jsonp : 'callback', // 回调函数名形参 url?callback = call_back
jsonpCallback: 'call_back', // 回调函数名实参
success: function(data, status) {
console.log('状态为:' + status);
console.log(data);
}
});
// });
}
</script>
</html>
- 服务端处理请求:
package com.api.controller;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
import org.springframework.web.bind.annotation.ResponseBody;
import com.alibaba.fastjson.JSON;
import com.api.common.ListResponse;
import com.api.model.Student;
import com.api.service.StudentService;
@Controller
@RequestMapping("/Student/")
public class StudentController {
@Autowired
StudentService studentService;
@RequestMapping(value = "getStudentList", produces="text/html;charset=UTF-8")
@ResponseBody
public String getStudentList(String callback) { // 回调函数名也可以写死
ListResponse<Student> studentList = studentService.getStudentList();
return callback+"("+JSON.toJSONString(studentList)+")";
}
}
- 服务端返回的数据格式如下:
call_back({
"items": [
{
"age": 21,
"classes": "173521班",
"id": 1,
"name": "张敏",
"sex": "女"
},
{
"age": 21,
"classes": "173521班",
"id": 2,
"name": "江南",
"sex": "男"
}
]
})
- 如上述代码所示jQuery实现jsonp调用的步骤是:
- 浏览器端定义回调函数作为url参数传送到服务端。url?callback = call_back。
- 服务端接收到参数后根据参数名+data数据封装成js函数格式的数据返回到浏览器端。
- 浏览器端接受到数据后执行回调函数,Jquery中为success函数。执行返回的js代码。
- 从而得到data数据。
JSONP是较为常用的一种跨域方式,不受浏览器兼容性的限制,然而它支持GET请求,RESTful架构并不适用。
4.3、通过CORS实现跨域(推荐)
跨域资源共享(Cross-Origin Resource Sharing)是由W3C提出的一个用于浏览器以XMLHttpRequest方式向其他源的服务器发起请求的规范。不同于JSONP,CORS是以Ajax方式进行跨域请求,需要服务端与客户端的同时支持。目前CORS在绝大部分现代浏览器中都是支持的。
- 前端ajax请求:
<html>
<body>
<div>
<button id="btn_cors" onclick="CORS_function()">CORS点击</button>
</div>
</body>
<script type = "text/javascript">
function CORS_function() {
$.ajax({
async: true,
url: "http://localhost:8080/ApiDemo_Yshow/Student/getStudentByName",
type: "GET",
dataType: "json",
data: {
"name": "江南天地"
},
success: function(data, status) {
console.log('状态为:' + status);
console.log(data);
}
});
}
</script>
</html>
- 服务端处理请求 - 方法1 - setHearder:
package com.api.controller;
import javax.servlet.http.HttpServletResponse;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.ResponseBody;
import com.api.common.SingleResponse;
import com.api.service.StudentService;
@Controller
@RequestMapping("/Student/")
public class StudentController {
@Autowired
StudentService studentService;
@RequestMapping(value = "getStudentByName")
@ResponseBody
public SingleResponse getStudentByName(@RequestParam(value = "name", required = false) String name,//
HttpServletResponse response) {
SingleResponse student = studentService.getStudentByName(name);
response.setHeader("Access-Control-Allow-Origin", "*");
response.setHeader("Access-Control-Allow-Methods", "GET, HEAD, POST, PUT, DELETE, TRACE, OPTIONS, PATCH");
return student;
}
}
- 服务端处理请求 - 方法2 - @CrossOrigin 注释(SpringMVC4.2以上)
package com.api.controller;
import org.springframework.beans.factory.annotation.Autowired;
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.RequestMethod;
import org.springframework.web.bind.annotation.ResponseBody;
import com.api.common.SingleResponse;
import com.api.service.StudentService;
@Controller
@RequestMapping("/Student/")
public class StudentController {
@Autowired
StudentService studentService;
@CrossOrigin(origins = "*", maxAge = 3600)
@RequestMapping(value = "getStudentByName",method = RequestMethod.POST)
@ResponseBody
public SingleResponse getStudentByName(String name) {
SingleResponse student = studentService.getStudentByName(name);
return student;
}
}
- 服务端处理请求-方法3 - 第三方CORSFilter - web.xml
<filter>
<filter-name>CorsFilter</filter-name>
<filter-class>org.apache.catalina.filters.CorsFilter</filter-class>
<init-param>
<param-name>cors.allowed.origins</param-name>
<param-value>*</param-value>
</init-param>
<init-param>
<param-name>cors.allowed.methods</param-name>
<param-value>GET,POST,HEAD,OPTIONS,PUT</param-value>
</init-param>
<init-param>
<param-name>cors.allowed.headers</param-name>
<param-value>Content-Type,X-Requested-With,accept,Origin,Access-Control-Request-Method,Access-Control-Request-Headers,自定义header</param-value>
</init-param>
<init-param>
<param-name>cors.exposed.headers</param-name>
<param-value>Access-Control-Allow-Origin,Access-Control-Allow-Credentials</param-value>
</init-param>
<init-param>
<param-name>cors.support.credentials</param-name>
<param-value>true</param-value>
</init-param>
<init-param>
<param-name>cors.preflight.maxage</param-name>
<param-value>10</param-value>
</init-param>
</filter>
<filter-mapping>
<filter-name>CorsFilter</filter-name>
<url-pattern>/*</url-pattern>
</filter-mapping>
五、参考资料
- [x] Jsonp解决ajax跨域问题
- [x] 跨域资源共享 CORS 详解
- [x] ajax jsonp跨域乱码解决方案
- [x] 解决ajax跨域的方法原理详解_cnsd
- [x] Ajax跨域问题的两种解决方法 _博客园
- [x] 说说JSON和JSONP,也许你会豁然开朗
- [x] SpringMVC开启CORS支持
- [x] java 服务端设置跨域
- [x] CORS解决ajax跨域问题
六、后记
本文仅用于学习笔记记录。初始版本比较粗糙,后续会不断完善。