-
为什么发生Ajax跨域?
1、浏览器显示
2、访问的路径不是本地的
3、发送的请求是XMLHttprequest的请求
查看全部 -
csrf filter
查看全部 -
为什么会发生ajax跨域/ 跨域的场景
总的来说, 产生跨域问题有3个原因: 浏览器的限制, 跨域, xhr(XMLHttpRequest) 请求; 这三个条件同时满足才会产生跨域安全问题
浏览器的限制
一句话解释就是: 因为发送请求时跨域了所以浏览器报跨域问题
跨域不成功和后台服务器没有关系, 不是因为后台不允许前台调用, 真正的原因是浏览器出于安全考虑, 会对发送的不同源跨域请求进行安全校验,校验不通过时就会报跨域错误。
这个时候可能你的请求已经发送成功并且返回了数据, 但控制台报了一条跨域错误, 说明服务器后台式没有任何限制的, 前台已经访问到了后台, 是浏览器报的错; 说白了就是浏览器多管闲事, 而不是后台不允许
跨域: 发出去的请求不是本域的. 前台发送的请求里面和后台的 协议, 域名, 端口任何一个不一样, 浏览器都会认为是跨域; 比如前台是localhost:8080端口, 请求的是localhost:8081端口 , 虽然域名相同, 但端口不一样, 所以跨域了;
发送的是 XHR(XMLHttpRequest) 请求 (也是最重要的原因); 只要发送的不是XHR请求, 比如发送的请求Type为jsonp, 就算是跨域了, 浏览器也不会报跨域安全问题
跨域解决方案
命令行启动参数修改
Cors, 在响应头里面添加字段
nodejs可以引入第三方包cors
nginx反向代理实现跨域(proxy_pass模块)
Vue.config.js里面配置devServer的proxy
谷歌的插件allow cors可以解决谷歌浏览器里面的跨域
webSocket本身不存在跨域的问题,所以我们可以利用webSocket来进行非同源中间的通信
从浏览器出发, 让浏览器不去做跨域的限制;
方法1: 浏览器禁止跨域安全检查
思路: 通过命令行修改浏览器启动参数, 让浏览器不进行跨域安全校验, 从而允许跨域;
做法: 命令行启动浏览器后添加参数 - -disable-web-security; 如 chrome --disable-web-security, 这个参数的作用是禁止浏览器进行跨域安全检查;
缺陷:
总结: 虽然可以解决跨域, 但是这种方式价值不大, 因为需要用户手动修改, 而且这种改动是客户端的改动, 不太实用, 而在实际项目中, 主要是对服务端进行修改来实现浏览器支持跨域;
每次都需要通过命令行启动参数来启动浏览器, 过于繁琐
该方法会导致稳定性和安全性有所下降
该方法是客户端方法的改动, 在实际使用中, 要每个客户端都禁止浏览器跨域检查是不太现实的, 实用性较低
JSONP (json with padding; jsonp 是json的一种补充使用) 避免发生跨域, 因为只有xhr请求才会可能发生跨域, 而jsonp 的请求类型不是xhr, 而且主要是通过动态创建script脚本, 利用script标签向不同源接口请求资源可以跨域的特点, 在script里面发送跨域请求, 获取其他来源动态生成的json数据, 所以不会产生跨域问题;
jsonp 与ajax 请求的区别
思路: JSONP是一个非官方协议, 是一个约定, 它约定如果发送请求的时候在script标签里携带一个 callback 参数, callback 的值是一个页面中定义好的函数名, 后台检测到 callback 就知道这是一个jsonp请求, 这时服务器返回的数据就会由原来的json对象改为js代码;
函数是浏览器定义的, 处理逻辑浏览器写; 数据是 服务器返回的, 到底是什么数据 服务器决定;
实现原理: 前台使用 ajax 的 get 请求, 将 dataType 设为 'jsonp' , 服务器创建一个类并继承抽象类 AbstractJsonResponseBodyAdvice, 最后注解 @ControllerAdvice;
具体实现流程:
补充: jsonp请求里除了callback参数外, 还一个下划线 _ 参数,参数值是一串随机数字, 它的作用主要是防止请求被缓存, 如果需要缓存的话, 可以在ajax请求里面加上 cache: true, 表示结果可以被缓存;
JSONP优缺点
JSONP优点是简单兼容性好,可用于解决主流浏览器的跨域数据访问的问题。
缺点:
不安全,可能会遭受XSS攻击;
发送的不是xhr请求; xhr的一些事件, 异步功能jsonp都没有;
仅支持get方法, 不能满足实际开发需求; jsonp是通过动态创建script发请求, script 只支持get
服务器需要改动代码来支持跨域, 当接口不是我们自己的代码无法改动时, jsonp就无能为力了;
声明一个回调函数,其函数名(如show)当做参数值,要传递给跨域请求数据的服务器,函数形参为服务器返回的data。
创建一个
<script>
标签,把跨域的API接口地址赋值给script的src 并且在这个地址中携带这个show 函数名(通过问号传参:?callback=show)。服务器接收到请求后,把传递进来的函数名和它需要给你的数据拼接成一个字符串,例如:传递进去的函数名是show,它准备好的数据是
show('你好么')
。服务器把数据返回给浏览器, 返回结果为js代码, js代码的内容是函数调用的形式, 函数名为callback 的值, 函数的参数为之前要返回的json对象, 浏览器就可以执行之前声明 的这个回调函数, 对返回的数据进行操作;
相当于向
http://localhost:3000/say?wd=Iloveyou&callback=show
这个地址请求数据,然后后台返回show('你好么')
,最后会运行show()这个函数,打印出'你好么'
ajax属于同源策略, jsonp属于非同源策略;
ajax的核心是通过XmlHttpRequest获取非本页内容,而jsonp的核心则是动态添加<script>标签来调用服务器提供的js脚本。
普通的ajax请求发出去的类型是 xhr, jsonp 请求发出去的类型是script, ajax请求返回的数据类型是json, jsonp返回类型是 js脚本;
产生跨域后解决
解决思路:
方式:
一. 从被调用方考虑,有三种情况,分别是服务器实现 (重点),nginx配置和apache配置。
原理: 在这种解决方案里面, 跨域请求是直接从调用方的浏览器发送到被调用方的http服务器, http服务器再把请求转发到应用服务器; 应用服务器处理完之后, 把响应返回给http服务器, http服务器再把响应转回到调用方的浏览器;
最终的目标是要在响应头里添加字段, 所以这里有两个地方可以增加响应头; 一个是中间的http服务器上增加, 也就是Nginx/ Apache; 一个是应用服务器, 也就是Tomcat
被调用方; 基于让被调用方支持跨域的思路 , 支持基于http协议关于跨域方面的要求; 也就是从A域名调用b域名的时候, 在b域名返回的响应头里添加指定字段, 告诉浏览器, 我允许A域名调用, 浏览器通过校验, 就不会报跨域错误;
调用方; 如果被调用方不是公司的, 无法进行修改, 就可以从调用方进行修改, 这种做法是隐藏跨域, 通过一个代理, 从浏览器里发出的都是A域名的请求, 通过这个代理把指定的url转到B域名里, 在浏览器看来它们就是同一个域名, 就没有跨域问题
被调用方解决: CORS
CORS 需要浏览器和后端同时支持。IE 8 和 9 需要通过 XDomainRequest 来实现。
浏览器会自动进行 CORS 通信,实现 CORS 通信的关键是后端;
服务端设置 Access-Control-Allow-Origin 就可以开启 CORS;
浏览器在发送跨域请求时, 会先判断请求是简单请求和非简单请求。 简单请求是先执行请求再验证,非简单请求是先验证再请求。 非简单请求实际上会发送两个请求, 第一次会先发送一个预检请求, 这个预检请求是浏览器额外发送的 options 预检请求, 检查通过之后, 才会真正把跨域请求发送出去;
预检命令的缓存: 非简单请求中添加的响应头, 用于缓存预检命令, 提高请求效率; 告诉浏览器在一个小时之内可以缓存预检命令的结果, 发送非简单请求时不用再发送预检命令 res.setHeader('Access-Control-Max-Age', 3600)
简单请求(比较常见):
方法为 get,head,post 中的一种;
请求header里面没有自定义头;
Content-Type的值为限于以下3者之一: text/plain, multipart/form-data, application/x-www-form-urlencoded
非简单请求(比较常见):
put, delect方法的ajax请求;
发送json格式的ajax请求;
带自定义头的ajax请求。
简单请求处理方案:在响应头中添加
**Access-Control-Allow-Origin=“允许跨域的url”**,即跨省域时,请求头Origin的值,所以一般是获取Origin的值。 **Access-Control-Allow-Method=“*”**,允许的方法。
非简单请求处理方案:响应头中添加
"*" 表示允许所有内容或类型; Access-Control-Allow-Origin=“允许跨域的url”,即跨域时,可以获取请求头Origin的值。 Access-Control-Allow-Method=“*”,// 允许所有方法 'Access-Control-Allow-Origin', * // 允许所有域 res.setHeader('Access-Control-Request-Headers', 'Content-Type') Access-Control-Request-Headers=“Content-Type" // 自定义的header的key 如: // 设置哪个域/ 源可以跨域调用 res.setHeader('Access-Control-Allow-Origin', origin) // 允许携带哪个头 可以跨域 res.setHeader('Access-Control-Allow-Headers', 'name') // 允许哪个方法 可以跨域 res.setHeader('Access-Control-Allow-Methods', 'PUT') // 允许携带cookie res.setHeader('Access-Control-Allow-Credentials', true) // 预检命令的缓存时间; 用于缓存预检命令, 提高请求效率; 告诉浏览器在一个小时之内可以缓存这段信息, 发送非简单请求时不用再发送预检命令 res.setHeader('Access-Control-Max-Age', 3600) // 允许返回的头 res.setHeader('Access-Control-Expose-Headers', 'name')
带cookies的跨域解决:在响应头添加
1) 带cookie的时候, origin 必须是全匹配, 不能使用*, 否则会报错, 并且要添加 Access-Control-Allow-Credentials="true" 的字段;
res.setHeader('Access-Control-Allow-Origin', "http://locahost: 8081") Access-Control-Allow-Credentials="true" // 允许发送请求时携带cookies
http里的会话, 也就是session, 是依赖于cookie的, sessionid存放在cookie中, 在实际工作中, 带cookie的跨域很重要;
2) 用 filter实现解决: 浏览器发现请求是跨域的时候, 会在请求头里添加origin字段, 这个字段有当前域的信息, 后台可以在filter里得到这个字段, 然后再把它写回到 Access-Control-Allow-Origin 里面, 这样就可以支持所有域的调用;
带自定义头的跨域
在jQuery里面, 设置自定义头有两种方法:
1) 在headers里添加;
2) XMLHttpRequest 对象提供了一个设置请求头的方法: setRequestHeader, 可以在 beforeSend 回调函数里用xhr.setRequestHeader("x-header2":, "aaa") 方法设置;
请求头里的自定义属性:
获取请求头的自定义属性 , 然后再把它写到响应头的 Access-Control-Allow-Origin 里面 , 就可以支持所有自定义头
nodejs可以引入第三方包cors
npm install --save cors
//CORS middleware var allowCrossDomain = function(req, res, next) { res.header('Access-Control-Allow-Origin', 'http://example.com'); res.header('Access-Control-Allow-Methods', 'GET,PUT,POST,DELETE'); res.header('Access-Control-Allow-Headers', 'Content-Type'); next(); } //... app.configure(function() { app.use(express.bodyParser()); app.use(express.cookieParser()); app.use(express.session({ secret: 'cool beans' })); app.use(express.methodOverride()); app.use(allowCrossDomain); app.use(app.router); app.use(express.static(__dirname + '/public')); });
被调用方解决: ngnix反向代理配置 实现跨域(proxy_pass模块):
虚拟主机: 多个域名指向同一个服务器, 服务器根据不同的域名把请求转到不同的应用服务器, 看上去好像有多个主机, 实际上只有一个主机;
server{ # 监听9099端口 listen 9099; # 域名是localhost server_name localhost; #凡是localhost:9099/api这个样子的,都转发到真正的服务端地址http://localhost:9871 location ^~ /api { proxy_pass http://localhost:9871; } }
被调用方- Apache解决
和 Nginx作用是一样的, 做法就是把Nginx上的配置再实现一次;
Vue中 webpack.config.js里面配置devServer的proxy
利用node + webpack + webpack-dev-server代理接口跨域。在开发环境下,由于vue渲染服务和接口代理服务都是webpack-dev-server同一个,所以页面与代理接口之间不再跨域,无须设置headers跨域信息了。后台可以不做任何处理。
module.exports = { entry: {}, module: {}, ... devServer: { historyApiFallback: true, proxy: [{ context: '/login', target: 'http://www.daxihong.com:8080', // 代理跨域目标接口 changeOrigin: true, secure: false, // 当代理某些https服务报错时用 cookieDomainRewrite: 'www.daxihong.com' // 可以为false,表示不修改 }], noInfo: true } }
谷歌的插件allow cors可以解决谷歌浏览器里面的跨域
webSocket本身不存在跨域的问题, 所以我们可以利用webSocket来进行非同源中间的通信
查看全部 -
经过创建的 过滤器的过滤操作后,filter 解决 了 浏览器对跨域请求的拦截,当有多个地址需要进行相同操作时,可以使用"*"代替具体的地址,从而实现大范围的跨域请求
查看全部 -
解决跨域问题①:
针对该浏览器添加参数,禁止浏览器做校验,避免报跨域问题
查看全部 -
修改浏览器的设置后,没有再出现跨域问题
由此可以认为,跨域问题主要还是在浏览器发生的校验失败等问题导致的,与后台没有任何关系
查看全部 -
发生Ajax 跨域请求的原因
查看全部 -
Ajax 跨域请求 限制原因:
3、XHR(XMLHttpRequest)请求:
测试: 新增加一个 <img>标签,重启启动后,由图可知,尽管新添加了一个 <img>标签,但是,浏览器所报的错误还是只有一个,而这一个便是之前的<a>标签的请求;而这是由于 <img> 和 <a> 两个请求发送时的 Type 不同,<img> 是json类型的,而<a>则是xhr类型的
查看全部 -
Ajax 跨域请求 限制原因:
2、跨域:
由图中可知,server 和 client 的 port 端口是不同的,因此,浏览器检测到后认为其发生了跨域
查看全部 -
Ajax 跨域请求 限制原因:
1、浏览器限制:
由图中可以知道,当发送请求时,尽管控制台报了跨域请求错误,但是IDE的服务器后台,依旧正常收到了请求,并打印相关 响应字段
查看全部 -
Ajax 跨域请求 限制原因:
1、浏览器限制:
由截图可知,服务器的请求响应状态为200,这代表这客户端的请求被服务器正常的响应了,服务器后台是没有任何限制,而且正确处理 了;从侧面也就证明了是 浏览器进行了限制,从而报了错误给我们
查看全部 -
引入jasmine 测试框架后,console 会生成一个界面用于提示用户测试结果及相关错误
查看全部 -
引入前端测试框架 jasmine,提高开发效率,从官网进入到 Releases目录下下载 jasmine .zip 压缩文件,解压后将lib目录下的包复制到工程项目的static目录下
查看全部 -
引入 jasmine 测试框架
查看全部 -
创建 ajaxClient 工程并编写 前台页面代码
查看全部
举报