同源政策
同源的概念:如果两个页面拥有相同的协议、域名和端口,那么这两个页面就属于同一个源,只要有一个不相同,就是不同源。
http://www.example.com/dir/page.html作比较(没写端口名就默认为80端口)
http://www.example.com/dir/other.html (同源)
http://example.com/dir/other.html (不同源,域名不同)
http://www.example.com:81/dir/page.html (不同源,端口号不同)
https://www.example.com/dir/page.html (不同源,协议不同)
同源政策
浏览器的同源策略,限制了来自不同源的"document"或脚本,对当前"document"读取或设置某些属性。从一个域上加载的脚本不允许访问另外一个域的文档属性。
Ajax请求限制
Ajax只能向自己的服务器发送请求。若是向非同源的服务器发送请求将会被拒绝。
解决方法
使用JSONP解决同源限制问题
1.将不同源的服务端请求地址写在script标签的属性中
<script src="www.example.com"></script>
<script src="https://cdn.bootcss.com/jquery/3.3.1/jquery.min.js"></script>
2.服务器端响应数据必须是一个函数的调用,真正要发送给客户端的数据需要作为函数调用的参数。(数据已返回回来就会用script标签标起来,当成js代码来执行)
const data = 'fn({name:'张三',age:'20'})';
//一到客户端页面就将在客户端页面中找到名为fn的函数并执行
res.send(data);
3.在客户端全局作用域下定义函数fn(一定要在script标签之前定义函数,否则会因为找不到fn函数而报错)
function fn (data) { /*处理服务器端返回的数据的代码*/}
4.在函数内部对服务器端返回的数据进行处理
function fn (data) { console.log(data);}
原理:jsonp(json with padding),不属于Ajax请求,它是利用script标签中的src属性,src可以跨域加载js脚本,返回的值为js代码,在数据请求回来后,会把内容放在script标签中当作JavaScript代码来执行。这完全避开了同源政策。
Jsonp代码的封装
要考虑的有:
1.动态发送请求(script要动态创建)
2.可复用性高
3.为jsonp与fn函数解绑,不要事先定义,由调用者传递
4.客户端的要定义的函数的改变不影响后端的代码
客户端的代码如下:
function jsonp (options) {
//解决每一次发送请求的回调函数名不能一样的问题,否则会发生覆盖
var funName = 'myfunction' +
Math.random().toString().replace('.','');
//将调用者传过来的success挂载到全局作用域上
Window[funName] = options.success;
var param = '';
//拼接请求参数
for (let key in options.data) {
param += '&' + key + '=' + options.data[key];
}
options.url = '?' + 'callback=' + funName + param;
//创建script标签
var script = document.createElement('script');
script.src = options.url;
//将script标签追加到页面中
document.body.appendChild(script);
script.onload = function () {
/*等到数据被加载完成后就删除script标签,
因为不删除的话有发送多次请求就会有多个script标签在body中,这是没有用处的
*/
document.body.removeChild(script);
}
}
var btn2 = document.getElementById('btn2');
//为按钮注册点击事件
btn2.onclick = function () {
jsonp({
url: 'http://www.baidu.com',
data:{
name:'lisi',
age:34
},
success:function (data){
console.log("success2")
}
})
}
服务器端的代码
import path from 'path';
import express from 'express'
//使用express框架创建web服务
const app = express();
//设置静态资源访问服务器功能,
//设置了之后,public目录下的文件可以直接用url访问
app.use(express.static(path.join(__dirname,'public')));
//设置路由
app.get('/',(req,res) =>{
const fnName = req.query.callback;
const data = JSON.stringify({name:'zhangsan',sex:'man'})
const result = fnName + '(' + data + ')'
setTimeout(() => {
res.send(result)
}, 1000);
})
app.listen(3001);//监听端口
服务器端代码的优化:
import path from 'path';
//使用express框架创建web服务
const app = express();
//设置静态资源访问服务器功能,
//设置了之后,public目录下的文件可以直接用url访问
app.use(express.static(path.join(__dirname,'public')));
//设置路由
app.get('/',(req,res) =>{
res.jsonp({name:'zhangsan',age:34})//实际上是做了上面一样的操作
})
app.listen(3001);//监听端口
服务器端的解决方案
原理:同源政策只限制客户端,服务器端可以跨域访问资源,所以我们可以在客户端照常发送Ajax请求,在服务器端借助requests模块跨域访问资源,然后将资源返回给客户端。
客户端代码:
ajax({
type:'get',
url:'http://localhost:3000/server',
data:{name:'zhangsan',age:23},
header:{
'Content-type':'application/json'
},
sucess:function (data,object) {console.log('success!');
},
error:function (data,object) {console.log('error!');
}
})
服务器端代码:
import path from 'path';
import express from 'express'
import request from 'request'
//使用express框架创建web服务
const app = express();
//设置静态资源访问服务器功能,
//设置了之后,public目录下的文件可以直接用url访问
app.use(express.static(path.join(__dirname,'public')));
//设置路由
app.get('/',(req,res) =>{
request('http://localhost:3001/server',(error,response,body){
res.send(body);
})
})
app.listen(3000);//监听端口
CORS 跨域资源共享
CORS:全称为Cross-origin resource sharing,它允许浏览器向跨域服务器发送Ajax请求,克服了Ajax只能同源使用的限制。
原理:实际上,客户端向浏览器端发送请求,会在请求头上注明origin(来源),无论是同源还是跨域请求资源,服务器端都会给出响应。但是跨域服务器会根据Acceess-Control-Access-Origin来判断origin(来自客户端),若不是可允许的源,服务器端返回的便不是数据而是错误信息,所以我们可以在服务器端设置Access-Control-Allow-Orgin属性,将客户端的地址写进去,便能够获取数据了。
在客户端照常发送数据,在服务器端配置,服务器端的代码如下:
import path from 'path';
import express from 'express'
//使用express框架创建web服务
const app = express();
//设置静态资源访问服务器功能,
//设置了之后,public目录下的文件可以直接用url访问
app.use(express.static(path.join(__dirname,'public')));
//拦截所有请求,为所有的请求设置响应头
app.use((req,res,next) => {
//配置允许的请求源,可以是多个地址,用逗号隔开,*表示允许所有的源
res.header('Access-Control-Allow-Orgin','*');
res.header('Access-Control-Allow-Methods','get,post');
next();//为请求放行,若是没有调用这个函数,那么请求不会往下去匹配
})
//设置路由
app.get('/',(req,res) =>{
var data = {name:'zhangsan',age:34};
res.send(data);
})
app.listen(3000);//监听端口
cookie
传统的客户端访问服务端使用http协议,这是无状态的,即客户端每次向服务器端响应都是以陌生主机的身份,这在某些情况下是不方便的,比如有时候需要服务器保留一些身份信息,为客户端保留某些状态,如添加的购物车,以及客户端的登录状态。为达到这个目的,我们可以使用cookie来保存信息,客户端首次访问浏览器,服务器会给客户端发送一个cookie,客户端下次访问时携带这个cookie,服务器就能认识了。
在使用Ajax技术发送跨域请求,默认情况下不会在请求中携带cookie信息。
withCredentials:指定在涉及跨域请求时,是否携带cookie信息,默认值为false
Access-Control-Allow-Credentials:true允许客户端发送请求时携带cookie
客户端核心代码:
var loginForm = document.getElementById('loginform') ;
//将html表单对象转换为fromData表单对象
var formData = new FormData(loginForm);
var xhr = new XMLHttpRequest();
xhr.open('post','http://localhost:3301/login');
//当发送跨域请求时携带cookie
xhr.withCredentials = true;
xhr.send(formData);
xhr.onload = function () {
console.log(xhr.responseText);
}
服务器端核心代码:
app.use((req,res,next) => {
//配置允许的请求源,可以是多个地址,用逗号隔开,*表示允许所有的源
res.header('Access-Control-Allow-Orgin','*');
res.header('Access-Control-Allow-Methods','get,post');
//允许客户端发送跨域请求时携带cookie信息
res.header('Access-Control-Allow-Credentials',true);
next();//为请求放行,若是没有调用这个函数,那么请求不会往下去匹配
})
来源:CSDN
作者:xiaofangmin
链接:https://blog.csdn.net/xiaofangmin/article/details/104486317