跨域、Ajax、Max-age和Expires

879 阅读6分钟

什么是跨域?

1.同源策略如下:

URL说明是否允许通信
http://www.a.com/a.js http://www.a.com/b.js同一域名下允许
http://www.a.com/lab/a.js http://www.a.com/script/b.js同一域名下不同文件夹允许
http://www.a.com:8000/a.js http://www.a.com/b.js同一域名,不同端口不允许
http://www.a.com/a.js https://www.a.com/b.js同一域名,不同协议不允许
http://www.a.com/a.js http://70.32.92.74/b.js域名和域名对应ip不允许
http://www.a.com/a.js http://script.a.com/b.js主域相同,子域不同不允许
http://www.a.com/a.js http://a.com/b.js同一域名,不同二级域名(同上)不允许(cookie这种情况下也不允许访问)
http://www.cnblogs.com/a.js http://www.a.com/b.js不同域名不允许

特别注意两点:

第一:如果是协议和端口造成的跨域问题“前台”是无能为力的;

第二:在跨域问题上,域仅仅是通过“URL的首部”来识别而不会去尝试判断相同的ip地址对应着两个域或两个域是否在同一个ip上。

同源策略限制以下几种行为

1.) Cookie、LocalStorage 和 IndexedDB 无法读取
2.) DOM 和 Js对象无法获得
3.) AJAX 请求不能发送

2.跨域解决方案

1、 通过jsonp跨域
2、 document.domain + iframe跨域
3、location.hash + iframe 
4、window.name + iframe跨域
5、postMessage跨域
6、 跨域资源共享(CORS
7、 nginx代理跨域
8、 nodejs中间件代理跨域
9、 WebSocket协议跨域

一、 通过jsonp跨域

通常为了减轻web服务器的负载,我们把jscssimg等静态资源分离到另一台独立域名的服务器上,在html页面中再通过相应的标签从不同域名下加载静态资源,而被浏览器允许,基于此原理,我们可以通过动态创建script,再请求一个带参网址实现跨域通信。

1.)原生实现:

 <script>
    var script = document.createElement('script');
    script.type = 'text/javascript';

    // 传参一个回调函数名给后端,方便后端返回时执行这个在前端定义的回调函数
    script.src = 'http://www.domain2.com:8080/login?user=admin&callback=handleCallback';
    document.head.appendChild(script);

    // 回调执行函数
    function handleCallback(res) {
        alert(JSON.stringify(res));
    }
 </script>

服务端返回如下(返回时即执行全局函数):

handleCallback({"status": true, "user": "admin"})

2.)vue.js:

this.$http.jsonp('http://www.domain2.com:8080/login', {
    params: {},
    jsonp: 'handleCallback'
}).then((res) => {
    console.log(res); 
})

后端node.js代码示例:

var querystring = require('querystring');
var http = require('http');
var server = http.createServer();

server.on('request', function(req, res) {
    var params = qs.parse(req.url.split('?')[1]);
    var fn = params.callback;

    // jsonp返回设置
    res.writeHead(200, { 'Content-Type': 'text/javascript' });
    res.write(fn + '(' + JSON.stringify(params) + ')');

    res.end();
});

server.listen('8080');
console.log('Server is running at port 8080...');

jsonp缺点:只能实现get一种请求。

二、 跨域资源共享(CORS)

普通跨域请求

只服务端设置Access-Control-Allow-Origin即可,前端无须设置,若要带cookie请求:前后端都需要设置。

需注意的是

由于同源策略的限制,所读取的cookie为跨域请求接口所在域的cookie,而非当前页。如果想实现当前页cookie的写入,可参考下文:七、nginx反向代理中设置proxy_cookie_domain 和 八、NodeJs中间件代理中cookieDomainRewrite参数的设置。

目前,所有浏览器都支持该功能(IE8+IE8/9需要使用XDomainRequest对象来支持CORS)),CORS也已经成为主流的跨域解决方案。

1、 前端设置:

1.)原生ajax

// 前端设置是否带cookie
xhr.withCredentials = true;

示例代码:

var xhr = new XMLHttpRequest(); // IE8/9需用window.XDomainRequest兼容

// 前端设置是否带cookie
xhr.withCredentials = true;

xhr.open('post', 'http://www.domain2.com:8080/login', true);
xhr.setRequestHeader('Content-Type', 'application/x-www-form-urlencoded');
xhr.send('user=admin');

xhr.onreadystatechange = function() {
    if (xhr.readyState == 4 && xhr.status == 200) {
        alert(xhr.responseText);
    }
};

2.)vue框架

a.) axios设置:

axios.defaults.withCredentials = true

b.) vue-resource设置:

Vue.http.options.credentials = true

2、 服务端设置:

若后端设置成功,前端浏览器控制台则不会出现跨域报错信息,反之,说明没设成功。

1.)Nodejs后台示例:

var http = require('http');
var server = http.createServer();
var qs = require('querystring');

server.on('request', function(req, res) {
    var postData = '';

    // 数据块接收中
    req.addListener('data', function(chunk) {
        postData += chunk;
    });

    // 数据接收完毕
    req.addListener('end', function() {
        postData = qs.parse(postData);

        // 跨域后台设置
        res.writeHead(200, {
            'Access-Control-Allow-Credentials': 'true',     // 后端允许发送Cookie
            'Access-Control-Allow-Origin': 'http://www.domain1.com',    // 允许访问的域(协议+域名+端口)
            /* 
             * 此处设置的cookie还是domain2的而非domain1,因为后端也不能跨域写cookie(nginx反向代理可以实现),
             * 但只要domain2中写入一次cookie认证,后面的跨域接口都能从domain2中获取cookie,从而实现所有的接口都能跨域访问
             */
            'Set-Cookie': 'l=a123456;Path=/;Domain=www.domain2.com;HttpOnly'  // HttpOnly的作用是让js无法读取cookie
        });

        res.write(JSON.stringify(postData));
        res.end();
    });
});

server.listen('8080');
console.log('Server is running at port 8080...');

Ajax简介

1.定义

Ajax 即“Asynchronous Javascript And XML”(异步 JavaScript 和 XML),是一种无需重新加载整个网页的情况下,能够更新部分网页的技术。

通过在后台与服务器进行少量数据交换,Ajax 可以使网页实现异步更新。这意味着可以在不重新加载整个网页的情况下,对网页的某部分进行更新。

2.Ajax 基本使用的四大步骤,简单易懂

第一步,创建xmlhttprequest对象,XMLHttpRequest对象用来和服务器交换数据。

var xmlhttp =new XMLHttpRequest();

第二步,使用xmlhttprequest对象的open()send()方法发送资源请求给服务器。

xmlhttp.open(method,url,async) 
//例如
xmlhttp.open(get, 'http://193.168.22.11:8000', true)

method包括get 和posturl主要是文件或资源的路径,async参数为true(代表异步)或者false(代表同步

第三步,使用xmlhttprequest对象的responseTextresponseXML属性获得服务器的响应。

第四步onReadyStateChange函数,当发送请求到服务器,我们想要服务器响应执行一些功能就需要使用onReadyStateChange函数,每次xmlhttprequest对象的readyState发生改变都会触发onReadyStateChange函数。

readyState属性,XMLHttpRequest对象的状态,改变从040代表请求未被初始化,1代表服务器连接成功,2请求被服务器接收,3处理请求,4请求完成并且响应准备。

/** 1. 创建连接 **/
var xhr = null;
xhr = new XMLHttpRequest()
/** 2. 连接服务器 **/
xhr.open('get', url, true)
/** 3. 发送请求 **/
xhr.send(null);
/** 4. 接受请求 **/
xhr.onreadystatechange = function(){
	if(xhr.readyState == 4){
		if(xhr.status == 200){
			success(xhr.responseText);
		} else { 
			/** false **/
			fail && fail(xhr.status);
		}
	}
}

Max-age和Expires

主要重点在于我们要明白一个相对(Expires)一个绝对(max-age).

一、Max-age

max-age是HTTP/1.1中,他是指我们的web中的文件被用户访问(请求)后的存活时间,是个相对的值,相对Request_time(请求时间).
例如:A.html  用户请求时间是18:00,max-age设置的是600的话,相当18:00+600秒过期,也就是相对18:00的时间后面600秒后过期.默认的max-age是由Expires算出来的.

二、Expires

Expires是HTTP/1.0中的,它比max-age要麻烦点.Expires指定的时间分下面二种,这个主要考虑到apache中设置是A还是M.

1.相对文件的最后访问时间(Atime)  当Apache使用A时间来做Expires时.这样设置时,他就和max-age的值相等,因为max-age是相对文件的请求时间(Atime)。

例如:ExpiresByType text/html A600

由上面我们得知,Apache设置Atime时,过期为600秒时。 Expires=18:00+600=18:10 max-age=18:00+600=18:10 得出:Expires=max-age

2.绝对修改时间(MTime)

这又分二种情况,我们来拿A.htm来讲:假设文件的建立时间18:00

  • 当用户Request请求为18:00时,过期为600秒 Expires=18:00+600=18:10 max-age=18:00+600=18:10 得出:Expires等于max-age

  • 当用户Request请求为18:20时,过期为600

    Expires=18:00+600=18:10(因为设置成Mtime时,时间由文件建立时间来决定max-age=18:20+600=18:30 得出:Expires不等于max-age

即:当使用MTime时,Expires的初始值为文件创建时间;而Max-age的初始值为客户端请求数据的时间.

另外要注意,像上面这种清况时,max-age优化,所以过期时间为18:30