美文网首页
JSONP和CORS两种跨域方式

JSONP和CORS两种跨域方式

作者: lueyoo | 来源:发表于2018-06-06 06:58 被阅读0次

    随着软件开发分工趋于精细,前后端开发分离成为趋势,前端同事负责前端页面的展示及页面逻辑处理,服务端同事负责业务逻辑处理同时通过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支持所有的浏览器请求类型,承载的请求数据量更大,开放更简洁,服务端只需要将处理后的数据直接返回,不需要再特殊处理。

    二、跨域解决方案距离

    2.1、JSONP方案实现跨域

    前段AJAX请求

    [ 复制代码

    ](javascript:void(0); "复制代码")

    <pre style="margin: 0px 0px 0px 22px; white-space: pre-wrap; word-wrap: break-word; font-size: 12px !important; font-family: "Courier New" !important;"> 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 })</pre>

    [ 复制代码

    ](javascript:void(0); "复制代码")

    服务端响应数据

    [ 复制代码

    ](javascript:void(0); "复制代码")

    <pre style="margin: 0px 0px 0px 22px; white-space: pre-wrap; word-wrap: break-word; font-size: 12px !important; font-family: "Courier New" !important;"> 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 }</pre>

    [ 复制代码

    ](javascript:void(0); "复制代码")

    注意内容:

    1、Ajax请求需要设置请求类型为Jsonp

    <pre style="margin: 0px 0px 0px 22px; white-space: pre-wrap; word-wrap: break-word; font-size: 12px !important; font-family: "Courier New" !important;">dataType: "jsonp"</pre>

    2、Ajax请求需要设置回调函数,当前函数值必须与服务器响应包含的callback名称相同

    <pre style="margin: 0px 0px 0px 22px; white-space: pre-wrap; word-wrap: break-word; font-size: 12px !important; font-family: "Courier New" !important;">jsonpCallback:"fn"</pre>

    3、Ajax请求可以设置jsonp(可选),传递给请求处理程序或页面,用以获得jsonp回调函数名的参数名,默认为:callback

    <pre style="margin: 0px 0px 0px 22px; white-space: pre-wrap; word-wrap: break-word; font-size: 12px !important; font-family: "Courier New" !important;">jsonp: "callback"</pre>

    4、服务端返回Json数据必须使用jsonpCallback设置的值进行包裹

    <pre style="margin: 0px 0px 0px 22px; white-space: pre-wrap; word-wrap: break-word; font-size: 12px !important; font-family: "Courier New" !important;">return "fn("+JsonUtil.objectToJson(outPut)+")"</pre>

    2.2、CORS方案实现跨域

    前段AJAX请求

    [[图片上传失败...(image-f88c85-1528237020998)]](javascript:void(0); "复制代码")

    <pre style="margin: 0px 0px 0px 22px; white-space: pre-wrap; word-wrap: break-word; font-size: 12px !important; font-family: "Courier New" !important;"> 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 }</pre>

    [[图片上传失败...(image-d70dd4-1528237020998)]](javascript:void(0); "复制代码")

    服务端响应数据

    [ 复制代码

    ](javascript:void(0); "复制代码")

    <pre style="margin: 0px 0px 0px 22px; white-space: pre-wrap; word-wrap: break-word; font-size: 12px !important; font-family: "Courier New" !important;"> 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 }</pre>

    [ 复制代码

    ](javascript:void(0); "复制代码")

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

    [ 复制代码

    ](javascript:void(0); "复制代码")

    <pre style="margin: 0px 0px 0px 22px; white-space: pre-wrap; word-wrap: break-word; font-size: 12px !important; font-family: "Courier New" !important;">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></pre>

    [ 复制代码

    ](javascript:void(0); "复制代码")

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

    [ 复制代码

    ](javascript:void(0); "复制代码")

    <pre style="margin: 0px 0px 0px 22px; white-space: pre-wrap; word-wrap: break-word; font-size: 12px !important; font-family: "Courier New" !important;"> 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
    23 * 24 * @author lu.wei
    25 * @email 1025742048@qq.com
    26 * @date 2017年1月4日
    27 * @since V1.0
    28 * @see com.luwei.console.mg.tag
    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 }</pre>

    [ 复制代码

    ](javascript:void(0); "复制代码")

    增加设置能够通过跨域访问的服务器地址

    <pre style="margin: 0px 0px 0px 22px; white-space: pre-wrap; word-wrap: break-word; font-size: 12px !important; font-family: "Courier New" !important;">#设置能够访问接口的域(多个通过都好分割)(不能配置127.0.0.1)
    CAN_ACCESS_DOMAIN=http://localhost:8020,http://localhost:9999,http://localhost:8080</pre>

    注意内容:

    1、Ajax请求必须要设置withCredentials属性为true

    <pre style="margin: 0px 0px 0px 22px; white-space: pre-wrap; word-wrap: break-word; font-size: 12px !important; font-family: "Courier New" !important;">withCredentials:true</pre>

    2、服务端需要配置过滤器,讲配置能够进行跨域访问服务器的地址进行配置

    <pre style="margin: 0px 0px 0px 22px; white-space: pre-wrap; word-wrap: break-word; font-size: 12px !important; font-family: "Courier New" !important;">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");</pre>

    3、withCredentials设置成true时,Access-Control-Allow-Origin不支持通过*的方式进行统配

    4、Access-Control-Allow-Origin不能直接配置多个请求服务器,但是可以通过静态配置多个的方式,然后根据referer匹配,匹配到哪个则设置Access-Control-Allow-Origin为哪个的方式来配置多个

    后记:

    jqGrid配置跨域请求的方式为:

    <pre style="margin: 0px 0px 0px 22px; white-space: pre-wrap; word-wrap: break-word; font-size: 12px !important; font-family: "Courier New" !important;">ajaxGridOptions: {
    xhrFields: {
    withCredentials: true }
    },</pre>

    相关文章

      网友评论

          本文标题:JSONP和CORS两种跨域方式

          本文链接:https://www.haomeiwen.com/subject/bpuvsftx.html