通过创建本地SCRIPT标签,将其属性设置为远程链接并将其append到head标签,可以动态跨域执行远程脚本。如果远程脚本内容是本地函数的调用,则可以跨域传递远程数据到本地,同时使用这种方式也可以间接达到读写cookie的目的。

这篇文章介绍下如何使用jQuery的jsonp进行跨域获取数据和读写cookie。

设置www.a.com和www.b.com的host指向127.0.0.1,端口分别为9001和9002,www.a.com域名将从www.b.com取数据并跨域读写www.b.com的cookie。

实现

  • a域前端控制器,核心方法getList
/**
 * 获取将要跨域的不同域名链接,传入username和token用于向不同域写cookie
 * @param request
 * @return
 * @throws Exception
 */
@RequestMapping(value = "getList", method = {RequestMethod.GET, RequestMethod.POST})
@ResponseBody
public String getList(HttpServletRequest request) throws Exception {
    JSONArray jsonArray = new JSONArray();
    jsonArray.add("http://www.b.com:9002/setCookie?token=123456&username="; + CookieUtils.get(request, "username"));
    String result = jsonArray.toJSONString();
    logger.info(result);
    return result;
}


/**
 * 手动从浏览器设置cookie
 * @param request
 * @param response
 * @return
 * @throws Exception
 */
@RequestMapping(value = "initCookie", method = {RequestMethod.GET, RequestMethod.POST})
@ResponseBody
public String initCookie(HttpServletRequest request, HttpServletResponse response) throws Exception {
    String name = request.getParameter("name");
    String value = request.getParameter("value");
    if(StringUtils.isNotBlank(name) && StringUtils.isNotBlank(value)) {
        logger.info("设置cookie: " + name + "---" + value) ;
        Cookie cookie = new Cookie(name, value);
        cookie.setDomain("a.com"); // 主域,不写"."开头,否则servlet3.1不兼容,没有点同样达到效果
        cookie.setPath("/");
        response.addCookie(cookie);
        return "yes";
    }
    return "no";
}

  • a域前端页面,getList拿到不同域的链接,使用$.ajax或$.getJSON方式实现jsonp请求
$.post('/getList', {}, function (data) { // 通过getList拿到需要跨域请求数据和写cookie的不同域名对应的链接,服务端返回JSONArray
    $.each(JSON.parse(data), function (index, content) {
        $.ajax({
            url:content,
            dataType:"jsonp", // 请求类型
            jsonp:"callback", // 向服务端传递callback本地函数名对应的参数名,默认为callback
            // jsonpCallback:"infoResp", // 回调的本地函数,不设置则在success中调用,均设置会先调用指定函数,再调用success;默认是jQuery随机生成
            success:function(data){
                console.warn('[success]server response: ' + JSON.stringify(data));
            }
        });


    })
})


function infoResp(data) {
    console.warn('[infoResp]server response: ' + JSON.stringify(data));
}

如果替换为$.getJSON形式

$.post('/getList', {}, function (data) {
    $.each(JSON.parse(data), function (index, content) {
        // jQuery内部替换入参值为?的第一个参数值,表示本地需回调的函数,是jQuery随机生成的,如jQuery321035583554815608265_1532929935172
        $.getJSON(content + "&jsoncallback=?", function (data) {
            $.each(data.success, function (i, item) {
                console.warn('[success]server response: ' + item.id + ", " + item.title);
            });
        });


    })
})

  • b域控制器
@RequestMapping(value = "/setCookie", method = {RequestMethod.GET, RequestMethod.POST})
@ResponseBody
public String setCookie(HttpServletRequest request, HttpServletResponse response, String token, String username) throws Exception {


    final String cookieKey = "username";
    // 验证token
    if (!"123456".equals(token)) {
        throw new IllegalArgumentException("token error");
    }
    // 写cookie
    Cookie cookie = new Cookie(cookieKey, username);
    cookie.setDomain("b.com");
    cookie.setPath("/");
    response.addCookie(cookie);


    // 也可以读出原有cookie作为数据返回,达到跨域读cookie的目的
    String previous = CookieUtils.get(request, cookieKey);
    logger.info("cookie值原有: " + previous + ", 现有: " + username);

    // 返回数据
    String callback = request.getParameter("callback");
    String data = null;
    if (StringUtils.isBlank(callback)) {
        callback = request.getParameter("jsoncallback");
    }
    if (StringUtils.isNotBlank(callback)) {
        JSONObject jsonObject = new JSONObject();
        //
        JSONArray jsonArray = new JSONArray();
        for (int i = 0; i < 3; i++) {
            JSONObject item = new JSONObject();
            item.put("id", i);
            item.put("title", "title-" + previous + "-" + i);
            jsonArray.add(item);
        }
        //
        jsonObject.put("success", jsonArray);
        data = jsonObject.toJSONString();
    } else {
        // 如果取不到本地callback的函数名,则提示
        callback = "alert";
        data = "'callback not found in request'";
    }


    String result = callback + "(" + data + ")";
    logger.info("result: " + result);
    return result;
}

验证

  1. 将b域名设置cookie,username=previous

  2. 访问 http://www.a.com:9001/initCookie?name=username&value=now ,将a域cookie设置为username=now

  3. 访问a域前端测试页面,后台js请求页面

http://login.a.com:9001/getList
http://www.b.com:9002/setCookie?token=123456&username=now&callback=jQuery321010174969742765616_1532937980789&_=1532937980790

控制态输出

[success]server response: {"success":[{"id":0,"title":"title-previous-0"},{"id":1,"title":"title-previous-1"},{"id":2,"title":"title-previous-2"}]}

也就是说在a域跨域取得了b域的cookie,username=previous,同时也传递了数据

  1. 访问 www.b.com ,查看请求,Cookie: username=now,即a域成功跨域写入b域的cookie

其他

通过jsonp获取跨域资源不需要设置Access-Control响应头

参考文章:

https://www.cnblogs.com/zhangruiqi/p/7880642.html

http://elf8848.iteye.com/blog/2067777

http://www.cnblogs.com/dowinning/archive/2012/04/19/json-jsonp-jquery.html