JSONP和CORS两种跨域方式的优缺点及使用方法原理介绍
随着软件开发分工趋于精细,前后端开发分离成为趋势,前端同事负责前端页面的展示及页面逻辑处理,服务端同事负责业务逻辑处理同时通过API为前端提供数据也为前端提供数据的持久化能力,考虑到前后端同事开发工具和习惯的不同,必然需要将前后端项目进行独立,再者考虑到网站访问速度的问题,需要将静态资源部署到CDN服务器上这样项目分离也成为了必然。然而项目分离部署分离带来的问题就是跨域请求的问题,本例对比较流行的两种跨域访问方式(Jsonp和CORS)进行讨论。
一、简要介绍
1.1、JSONP
JSONP是利用浏览器对script的资源引用没有同源限制,通过动态插入一个script标签,当资源加载到页面后会立即执行的原理实现跨域的。JSONP是一种非正式传输协议,该协议的一个要点就是允许用户传递一个callback或者开始就定义一个回调方法,参数给服务端,然后服务端返回数据时会将这个callback参数作为函数名来包裹住JSON数据,这样客户端就可以随意定制自己的函数来自动处理返回数据了。
JSONP只支持GET请求而不支持POST等其它类型的HTTP请求,它只支持跨域HTTP请求这种情况,不能解决不同域的两个页面之间如何进行JavaScript调用的问题,JSONP的优势在于支持老式浏览器,弊端也比较明显:需要客户端和服务端定制进行开发,服务端返回的数据不能是标准的Json数据,而是callback包裹的数据。
1.2、CORS
CORS是现代浏览器支持跨域资源请求的一种方式,全称是"跨域资源共享"(Cross-origin resource sharing),当使用XMLHttpRequest发送请求时,浏览器发现该请求不符合同源策略,会给该请求加一个请求头:Origin,后台进行一系列处理,如果确定接受请求则在返回结果中加入一个响应头:Access-Control-Allow-Origin;浏览器判断该相应头中是否包含Origin的值,如果有则浏览器会处理响应,我们就可以拿到响应数据,如果不包含浏览器直接驳回,这时我们无法拿到响应数据。
CORS与JSONP的使用目的相同,但是比JSONP更强大,CORS支持所有的浏览器请求类型,承载的请求数据量更大,开放更简洁,服务端只需要将处理后的数据直接返回,不需要再特殊处理。
1.3、两者之间的优缺点:
- JSONP的主要优势在于对浏览器的支持较好;虽然目前主流浏览器支持CORS,但IE10以下不支持CORS。
- JSONP只能用于获取资源(即只读,类似于GET请求);CORS支持所有类型的HTTP请求,功能完善。(这点JSONP被玩虐,但大部分情况下GET已经能满足需求了)
- JSONP的错误处理机制并不完善,我们没办法进行错误处理;而CORS可以通过onerror事件监听错误,并且浏览器控制台会看到报错信息,利于排查。
- JSONP只会发一次请求;而对于复杂请求,CORS会发两次请求。
- 始终觉得安全性这个东西是相对的,没有绝对的安全,也做不到绝对的安全。毕竟JSONP并不是跨域规范,它存在很明显的安全问题:callback参数注入和资源访问授权设置。CORS好歹也算是个跨域规范,在资源访问授权方面进行了限制(Access-Control-Allow-Origin),而且标准浏览器都做了安全限制,比如拒绝手动设置origin字段,相对来说是安全了一点。
但是回过头来看一下,就算是不安全的JSONP,我们依然可以在服务端端进行一些权限的限制,服务端和客户端也都依然可以做一些注入的安全处理,哪怕被攻克,它也只能读一些东西。就算是比较安全的CORS,同样可以在服务端设置出现漏洞或者不在浏览器的跨域限制环境下进行攻击,而且它不仅可以读,还可以写。
二、跨域解决方案距离
2.1、JSONP方案实现跨域
前段AJAX请求

1 $.ajax({
2 url: "http://otherdomain.com/manage/role/get",
3 async: false,
4 type: "get", 5 dataType: "jsonp",
6 data:{
7 "id":1
8 },
9 jsonp: "callback",
10 jsonpCallback:"fn",
11 success: function(data){
12 alert(data.code);
13 },
14 error: function(){
15 alert('fail');
16 }
17 })

服务端响应数据

1 @RequestMapping("/manage/role/get")
2 @ResponseBody
3 public String get(HttpServletRequest request, HttpServletResponse response) {
4 BaseOutput outPut = new BaseOutput();
5 try {
6 QueryFilter filter = new QueryFilter(request);
7 logger.info(filter.toString());
8 String id = filter.getParam().get(MainConst.KEY_ID);
9 if(!StringUtil.isEmpty(id)) {
10 ImRole role = roleService.getByPk(filter);
11 outPut.setData(role);
12 }
13 else {
14 outPut.setCode(OutputCodeConst.INPUT_PARAM_IS_NOT_FULL);
15 outPut.setMsg("The get id is needed.");
16 }
17 } catch (Exception e) {
18 logger.error("获取角色数据异常!", e);
19 outPut.setCode(OutputCodeConst.UNKNOWN_ERROR);
20 outPut.setMsg("获取角色数据异常! " + e.getMessage());
21 }
22 return "fn("+JsonUtil.objectToJson(outPut)+")";
23 }

注意内容:
1、Ajax请求需要设置请求类型为Jsonp
dataType: "jsonp"
2、Ajax请求需要设置回调函数,当前函数值必须与服务器响应包含的callback名称相同
jsonpCallback:"fn"
3、Ajax请求可以设置jsonp(可选),传递给请求处理程序或页面,用以获得jsonp回调函数名的参数名,默认为:callback
jsonp: "callback"
4、服务端返回Json数据必须使用jsonpCallback设置的值进行包裹
return "fn("+JsonUtil.objectToJson(outPut)+")"
2.2、CORS方案实现跨域
前段AJAX请求

1 function test() {
2 $.ajax({
3 url: "http://localhost:8080/AdsServer/manage/role/get",
4 type: "get",
5 async: false,
6 data:{
7 "id":1
8 },
9 dataType:"json",
10 withCredentials:true,
11 success: function(data){
12 alert(data);
13 alert(data.code);
14 },
15 error: function(){
16 alert('fail');
17 }
18 })
19 }

服务端响应数据

1 @RequestMapping("/manage/role/get")
2 @ResponseBody
3 public String get(HttpServletRequest request, HttpServletResponse response) {
4 BaseOutput outPut = new BaseOutput();
5 try {
6 QueryFilter filter = new QueryFilter(request);
7 logger.info(filter.toString());
8 String id = filter.getParam().get(MainConst.KEY_ID);
9 if(!StringUtil.isEmpty(id)) {
10 ImRole role = roleService.getByPk(filter);
11 outPut.setData(role);
12 }
13 else {
14 outPut.setCode(OutputCodeConst.INPUT_PARAM_IS_NOT_FULL);
15 outPut.setMsg("The get id is needed.");
16 }
17 } catch (Exception e) {
18 logger.error("获取角色数据异常!", e);
19 outPut.setCode(OutputCodeConst.UNKNOWN_ERROR);
20 outPut.setMsg("获取角色数据异常! " + e.getMessage());
21 }
22 return JsonUtil.objectToJson(outPut);
23 }

服务端增加过滤拦截器(web.xml)

1 <filter>
2 <filter-name>crossDomainFilter</filter-name>
3 <filter-class>com.luwei.core.filter.CrossDomainFilter</filter-class>
4 </filter>
5 <filter-mapping>
6 <filter-name>crossDomainFilter</filter-name>
7 <url-pattern>*</url-pattern>
8 </filter-mapping>

服务端增加过滤拦截器(java)

1 package com.luwei.core.filter;
2
3 import java.io.IOException;
4
5 import javax.servlet.Filter;
6 import javax.servlet.FilterChain;
7 import javax.servlet.FilterConfig;
8 import javax.servlet.ServletException;
9 import javax.servlet.ServletRequest;
10 import javax.servlet.ServletResponse;
11 import javax.servlet.http.HttpServletRequest;
12 import javax.servlet.http.HttpServletResponse;
13
14 import org.apache.commons.lang.StringUtils;
15 import org.slf4j.Logger;
16 import org.slf4j.LoggerFactory;
17
18 import com.luwei.console.mg.constant.ApplicationConfiConst;
19
20 /**
21 *
22 * <Description> TODO<br>
23 *
24 * @author lu.wei<br>
25 * @email 1025742048@qq.com <br>
26 * @date 2017年1月4日 <br>
27 * @since V1.0<br>
28 * @see com.luwei.console.mg.tag <br>
29 */
30 public class CrossDomainFilter implements Filter {
31 private Logger logger = LoggerFactory.getLogger(getClass());
32
33 public void doFilter(ServletRequest req, ServletResponse res, FilterChain chain) throws IOException, ServletException {
34 ApplicationConfiConst confiConst = (ApplicationConfiConst) ContextUtil.getBean("applicationConfiConst");
35 HttpServletResponse response = (HttpServletResponse) res;
36 HttpServletRequest request = (HttpServletRequest) req;
37 String referer = request.getHeader("referer");
38 String origin = null;
39 if (null != referer) {
40 String[] domains = confiConst.getCanAccessDomain().split(",");
41 for (String domain : domains) {
42 if (StringUtils.isNotEmpty(domain) && referer.startsWith(domain)) {
43 origin = domain;
44 break;
45 }
46 }
47 }
48 response.setHeader("Access-Control-Allow-Origin", origin);
49 response.setHeader("Access-Control-Allow-Methods", "POST, GET, OPTIONS, DELETE,PATCH");
50 response.setHeader("Access-Control-Max-Age", "3600");
51 response.setHeader("Access-Control-Allow-Headers", "x-requested-with");
52 // 是否支持cookie跨域
53 response.addHeader("Access-Control-Allow-Credentials", "true");
54
55 String requestURI = ((HttpServletRequest) req).getRequestURI();
56 long begin = System.currentTimeMillis();
57 chain.doFilter(req, res);
58 if (logger.isDebugEnabled()) {
59 logger.debug("[Request URI: " + requestURI + "], Cost Time:" + (System.currentTimeMillis() - begin) + "ms");
60 }
61 }
62 }

增加设置能够通过跨域访问的服务器地址
#设置能够访问接口的域(多个通过都好分割)(不能配置127.0.0.1)
CAN_ACCESS_DOMAIN=http://localhost:8020,http://localhost:9999,http://localhost:8080
注意内容:
1、Ajax请求必须要设置withCredentials属性为true
withCredentials:true
2、服务端需要配置过滤器,讲配置能够进行跨域访问服务器的地址进行配置
response.setHeader("Access-Control-Allow-Origin", origin);
response.setHeader("Access-Control-Allow-Methods", "POST, GET, OPTIONS, DELETE,PATCH");
response.setHeader("Access-Control-Max-Age", "3600");
response.setHeader("Access-Control-Allow-Headers", "x-requested-with");
// 是否支持cookie跨域
response.addHeader("Access-Control-Allow-Credentials", "true");
3、withCredentials设置成true时,Access-Control-Allow-Origin不支持通过*的方式进行统配
4、Access-Control-Allow-Origin不能直接配置多个请求服务器,但是可以通过静态配置多个的方式,然后根据referer匹配,匹配到哪个则设置Access-Control-Allow-Origin为哪个的方式来配置多个
后记:
jqGrid配置跨域请求的方式为:
ajaxGridOptions: {
xhrFields: {
withCredentials: true
}
},
JSONP和CORS两种跨域方式的优缺点及使用方法原理介绍的更多相关文章
- JSONP和CORS两种跨域方式的简单介绍和解决方案实例
随着软件开发分工趋于精细,前后端开发分离成为趋势,前端同事负责前端页面的展示及页面逻辑处理,服务端同事负责业务逻辑处理同时通过API为前端提供数据也为前端提供数据的持久化能力,考虑到前后端同事开发工具 ...
- Javascript几种跨域方式总结
在客户端编程语言中如javascript,同源策略规定跨域之间的脚本是隔离的,一个域的脚本不能访问和操作另外一个域的绝大部分属性和方法.只有当两个域具有相同的协议,相同的主机,相同的端口时,我们就认定 ...
- 从壹开始前后端分离【 .NET Core2.0 +Vue2.0 】框架之十二 || 三种跨域方式比较,DTOs(数据传输对象)初探
更新反馈 1.博友@落幕残情童鞋说到了,Nginx反向代理实现跨域,因为我目前还没有使用到,给忽略了,这次记录下,为下次补充.此坑已填 2.提示:跨域的姊妹篇——<三十三║ ⅖ 种方法实现完美跨 ...
- Ajax操作如何实现跨域请求 (JSONP和CORS实现Ajax跨域的原理)
由于浏览器存在同源策略机制,同源策略阻止ajax (XMLHttpRequest) 从一个源加载的文档或脚本获取或设置另一个源加载的文档的属性. 特别的:由于同源策略是浏览器的限制,所以请求的发送和响 ...
- jsonp的三种跨域方式
1.通过jq的$.ajax()完成跨域,这是我比较喜欢的一种方式. 代码如下: $.ajax({ type:'get', async:true, url:'地址', dataType:'jsonp', ...
- 「JavaScript」JS四种跨域方式详解
原文地址https://segmentfault.com/a/1190000003642057 超详细并且带 Demo 的 JavaScript 跨域指南来了! 本文基于你了解 JavaScript ...
- 「JavaScript」四种跨域方式详解
超详细并且带 Demo 的 JavaScript 跨域指南来了! 本文基于你了解 JavaScript 的同源策略,并且了解使用跨域跨域的理由. 1. JSONP 首先要介绍的跨域方法必然是 JSON ...
- java web 的 几种跨域方式
- Api之Cors跨域以及其他跨域方式
Web Api之Cors跨域以及其他跨域方式(三) 我们知道ajax不能跨域访问,但是有时我们确实需要跨域访问获取数据,所以JSONP就此诞生了,其本质使用的是Script标签,除JSONP以外还 ...
随机推荐
- JavaWeb从开发环境搭建,到第一个servlet程序(图文)
## 开学到今天,已经是第三周了~ 然而这门课的教材还没发~ 滋滋滋 表示很“蓝瘦”~~~ Java Web开发环境搭建 1. 下载安装Tomcat 官网地址:http://tomcat. ...
- 一道令人抓狂的零一背包变式 -- UVA 12563 Jin Ge Jin Qu hao
题目链接: https://uva.onlinejudge.org/index.php?option=com_onlinejudge&Itemid=8&page=show_proble ...
- kaggle之泰坦尼克号乘客死亡预测
目录 前言 相关性分析 数据 数据特点 相关性分析 数据预处理 预测模型 Logistic回归训练模型 模型优化 前言 一般接触kaggle的入门题,已知部分乘客的年龄性别船舱等信息,预测其存活情况, ...
- Ubuntu server 16.04安装,无网卡驱动解决
因为使用一个软件必须要在ubuntu server 16.04上安装,因此先在裸机上安装ubuntu server 16.04,具体信息: 镜像版本:ubuntu-16.04.6-server-amd ...
- Java学习之软件安装
成功安装了jdk-10.0.1.eclipse-committers-2018-09-win32-x86_64.mysql-5.7.18.1和tomcat-9.0.0.M17
- 二叉查找树的C++实现
#include <iostream> #include <algorithm> #include <stack> using namespace std; /// ...
- 数据分析——matplotlib
基础 # coding=utf-8 import matplotlib.pyplot as pt import numpy as np from matplotlib import font_mana ...
- :nth-child() 与 :nth-of-type(n)的区别
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8&quo ...
- 原生js的联动全选
开发应用中有很多工具可以使用,下面介绍一个原生js写的联动全选思路!!! <!DOCTYPE html> <html lang="en"> <head ...
- Spring + SpringMVC + Mybatis项目中redis的配置及使用
maven文件 <!-- redis --> <dependency> <groupId>redis.clients</groupId> <art ...