js跨域分析、jsonp的原理及两种实现方式

js跨域分析

上节课我们一起学习了登录,登录的时候我们把token写到了cookie当中,登录成功后,我们查看Cookie信息,如下图所示,发现是有token信息的。

我们回到登录页面,可以看到token信息。如下图所示。可以看到与淘淘商城首页的token信息是完全一致的。

不同的端口就已经属于跨域了,而我们就是要解决跨域问题。

首先我们修改下淘淘商城首页的登录和注册链接地址,因为目前这两个链接地址都不正确。

我们从淘淘商城首页的index.jsp页面代码说起,如下图所示。可以看到,商城首页的信息都在header.jsp页面当中。

我们找到header.jsp页面,发现,登录和注册在shortcut.jsp页面当中,如下图所示。

在shortcut.jsp页面当中,我们可以看到登录和注册信息,它们都是一个超链接,点击它们的时候会去触发js方法,如下图所示。

但我们惊奇的发现,在shortcut.jsp页面当中根本就没有js代码,那么js在哪儿呢?由于shortcut.jsp是header.jsp页面的一部分,而header.jsp是index.jsp的一部分,因此我们按照由低到高的顺序来找,在header.jsp页面当中,我们可以看到引用了base-v1.js的脚本文件,如下图所示。

那么我们便来看下这个脚本文件,我们到js目录下找到该脚本文件,打开它,如下图所示,可以看到当前login和regist方法的链接地址确实是不正确的。
修改下链接地址,如下:
function login() {
return location.href = “http://localhost:8088/page/login”;
}
function regist() {
return location.href = “http://localhost:8088/page/register”;
}
修改完这段js代码之后,我们来刷新淘淘商城首页(不用重启工程),然后点击登录和注册按钮,发现都可以正常跳转到相应的页面(这里就不贴图了)。

那么,我们要在淘淘商城首页拿到登录用户的信息的话,需要先拿到token,然后根据token调用单点登录系统的接口来获取用户信息。那么,这在淘淘商城的代码是怎样的呢?我们先找到footer.jsp(这是淘淘商城页脚的页面,所有页面都会引入这个页脚),在footer.jsp当中可以看到引入了taotao.js脚本文件,如下图所示。

我们打开taotao.js脚本文件,如下图所示,可以看到,这个文件当中是处理获取用户登录信息的,可以看到,token是可以从cookie中直接获取到的,如果能获取到token,那么就拿token去调用单点登录系统的相应接口,只是ajax请求地址不正确,我们需要修改一下。

还有,我们现在还不熟悉jsonp,因此我们暂且先把dataType : "jsonp"注释掉,修改后的checkLogin方法如下:

checkLogin : function(){
var _ticket = $.cookie(“TT_TOKEN”);
if(!_ticket){
return ;
}
$.ajax({
url : “http://localhost:8088/user/token/” + _ticket,
//dataType : “jsonp”,
type : “GET”,
success : function(data){
if(data.status == 200){
var username = data.data.username;
var html = username + “,欢迎来到淘淘!<a href=“http://www.taotao.com/user/logout.html” class=“link-logout”>[退出]”;
$("#loginbar").html(html);
}
}
});
}

那么现在能不能正常取到用户信息呢?我们来试一下,我们到淘淘商城首页,刷新首页,首先看一下当前有没有token信息,如下图所示,发现是有token信息的。这里啰嗦一句的是,能够看到cookie并不代表该cookie一定能用,因为有可能该cookie中的token信息在缓存redis当中已经过期了,不过过期了也没关系,拿着过期的token去获取用户信息返回的TaotaoResult中状态码是400,而不是200,而上面的代码判断只有状态码是200才将登录和注册换成用户名和退出的。

接着我们查看Console一栏,发现报错了,报的错误当中包含"Access-Control-Allow-Origin",这就说明跨域了!为什么会跨域呢?这是因为我们在taotao-portal-web工程访问taotao-sso-web工程,这两个工程的端口号是不一样的,一个是8082,另一个是8088,这就涉及到了跨域,而js从设计之初就不支持跨域的。

既然有了跨域问题,我们总得解决,目前大多数解决跨域的问题都是使用的jsonp方式,jsonp是利用了js的一个特点(或者你可以说是一个漏洞)来实现跨域。

我们先看下访问请求是否正确,同时也看下token是否过期,我们点击错误当中的 http://localhost:8088/user/token/d4205893-c717-4d97-aa90-ba23589fd76e这个链接,看到下面的结果说明该token还未过期。

本小节就到这儿,关于jsonp我们下节继续介绍。

jsonp的原理及两种实现方式

首先,说下什么是跨域?有以下两种:
1.域名不同
2.域名相同,端口不同

接着说下什么是jsonp?
jsonp不是新技术,它只是一种解决方案,使用js的特性绕过跨域请求,利用的特性便是js可以跨域加载js文件!!举个非常常见的例子,我们在html头部一般都会引入很多js,甚至我们直接引用在线的js,比如我们引用官方网站的jQuery路径加载进来也是可以的。JQuery的官方域名与我们的工程所在的域名肯定是不一样的,但是不影响使用,这就是我们所说的js可以跨域请求js文件!

跨域的特点是什么呢?我们可以在"Network"一栏看到请求的token信息,在Headers当中发现服务端已经正常响应了,如下图所示。

我们再看下Response一栏,发现没有任何信息,其实流程是这样的,我们使用ajax调用服务端的接口,服务端不管你请求是否是同一个域下,只管响应,于是 我们可以看到服务端响应的头信息,但是当数据经过浏览器时,浏览器判断出js访问的数据来自不同的域,于是便拒绝将数据返给页面。我们拿不到Response信息,也就无法展示内容了。

那么jsonp是怎样解决跨域问题的呢?流程图如下,图的上半部分展示的是传统跨域请求,这种请求我们是获取不到Response数据的,下半部分是介绍jsonp跨域获取数据的方案的。我们现在要跨域获取信息的js当中添加一个函数,该函数有返回值参数data,发起跨域请求的一端引入服务端定义好的一个js文件,请求的参数中是带callback回调函数的,并且callback=mycall中的"mycall"一定是与我们添加的那个函数的名称一致的,服务端接收到请求,就进行响应,并且判断参数中是否有callback参数,如果有callback参数的话,就对要返回的数据进行处理,加工成一段js代码(很简单,就是把一个json串包装成一个js函数,如下图mycall({id:1,name:z});)然后响应,js本来就是要请求访问服务端的一个js文件,现在返回的也是js代码,于是浏览器将不再进行拦截,由于返回的是js代码,而且js语句有个特点就是,一旦响应到浏览器端便立即执行,我们已经添加了该函数,于是便调用我们添加的那个函数,我们只需把数据做下处理并进行展示就可以了!!!
在这里插入图片描述

下面我们便来实现jsonp请求,上图所介绍的方法是比较复杂的,如果我们用jquery的话,如下图所示。
在这里插入图片描述

这样就会帮我们省很多事,我们只需要在ajax请求时指定dataType为jsonp,如下图所示。jquery便会帮我们自动创建一个函数。

我们刷新下淘淘商城首页,可以看到请求中自动为我们加上了回调函数,而且随机为我们添加了函数名为"jQuery3257776"的函数。

现在我们要做的便是修改服务端的代码,以便能够配合客户端完成整个请求过程,修改的方法如下图所示,我们将原来返回一个Map类型改为返回String类型,而且添加了callback参数,判断下是否有callback参数,如果有就说明是jsonp请求,我们要将result转换成json串并且包装成一个js函数返回。如果不是jsonp请求,那就直接将result转换成json串返回。

修改后代码如下:

@RequestMapping(value = “/user/token/{token}”,method = RequestMethod.GET)
@ResponseBody
public String getUserByToken(@PathVariable String token,String callback){
TaotaoResult result = userService.getUserByToken(token);
if(StringUtils.isNotBlank(callback)){
return callback+"("+JSON.toJSONString(result)+");";
}
return JSON.toJSONString(result);
}
好了,代码修改完了,我们现在重启taotao-sso-web工程,重启后,我们刷新淘淘商城首页,发现页面正常显示用户的姓名了!!注意,如果你这时还显示不出来,是因为token已经过期了,你需要重新登录一下,这样就可以看到了。

上面介绍的是最通用的一种方法,还有一种方法也可以解决,只是要求Spring的版本是4.1以上,还好,我们的淘淘商城用的版本是4.2,因此是没问题的,要修改的还是UserController,如下图所示。

修改后代码如下,由于result和mappingJacksonValue 属于不同类型,因此返回值类型修改成了Object。

@RequestMapping(value = “/user/token/{token}”,method = RequestMethod.GET)
@ResponseBody
public Object getUserByToken(@PathVariable String token,String callback){
TaotaoResult result = userService.getUserByToken(token);
if(StringUtils.isNotBlank(callback)){
MappingJacksonValue mappingJacksonValue = new MappingJacksonValue(result);
mappingJacksonValue.setJsonpFunction(callback);
return mappingJacksonValue;
}
return result;
}
我们重启下taotao-sso-web工程,然后重新刷新下淘淘商城首页,如下图所示,发现也可以正常显示用户名!

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值