跨域问题

问题引发思考,思考提升学习

1:什么是跨域问题

2:为什么会有跨域问题

3:跨域是为了什么

4:如何跨域

5:不同跨域方式的优缺点,如何选择正确的跨域方式

6:实现跨域应当注意什么

  • 跨域问题

由于浏览器同源策略,发送请求url的协议、域名、端口三个方面任意一与当前页面不相同即跨域。

  • 为什么会有跨域问题

1:什么是源

源(origin)就是协议、域名和端口号

2:什么是同源策略

因为存在浏览器同源策略,所以才会有跨域问题。那么浏览器是出于何种原因会有跨域的限制呢。其实不难想到,跨域限制主要的目的就是为了用户的上网安全。

如果浏览器没有同源策略,会存在什么样的安全问题呢。下面从 DOM 同源策略和 XMLHttpRequest 同源策略来举例说明:

如果没有 DOM 同源策略,也就是说不同域的 iframe 之间可以相互访问,那么黑客可以这样进行攻击:

  1. 做一个假网站,里面用 iframe 嵌套一个银行网站 http://mybank.com
  2. 把 iframe 宽高啥的调整到页面全部,这样用户进来除了域名,别的部分和银行的网站没有任何差别。
  3. 这时如果用户输入账号密码,我们的主网站可以跨域访问到 http://mybank.com 的 dom 节点,就可以拿到用户的账户密码了。

如果 XMLHttpRequest 同源策略,那么黑客可以进行 CSRF(跨站请求伪造) 攻击:

  1. 用户登录了自己的银行页面 http://mybank.comhttp://mybank.com 向用户的 cookie 中添加用户标识。
  2. 用户浏览了恶意页面 http://evil.com,执行了页面中的恶意 AJAX 请求代码。
  3. http://evil.com 向 http://mybank.com 发起 AJAX HTTP 请求,请求会默认把 http://mybank.com 对应 cookie 也同时发送过去。
  4. 银行页面从发送的 cookie 中提取用户标识,验证用户无误,response 中返回请求数据。此时数据就泄露了。
  5. 而且由于 Ajax 在后台执行,用户无法感知这一过程。

因此,有了浏览器同源策略,我们才能更安全的上网。

同源策略主要带来三个方面的限制:

1、cookie,localstorage和IndexDB无法读取

2、DOM无法获取

3、Ajax请求不能发送

由于浏览器同源策略,凡是发送请求url的协议、域名、端口三者之间任意一与当前页面地址不同即为跨域

  • 跨域是为了什么

为了能够使得使用cookie,让浏览器能够将sessionid放入到request。服务端能够识别是那个用户登录的。从而跨域也可以请求资源,并且不需要登录。

  • 如何跨域

跨域的方法:jsonp,jquery,ifram,cors。应用比较广泛的是cors,下面我们主要围绕cors来介绍如何跨域。

cors跨域:跨来源资源共享(Cross-Origin Resource Sharing(CORS)):

1:浏览器端支持情况

- IE8 和 IE9 通过 XDomainRequest 插件支持CORS,IE10 开始则完全正常支持CORS。 
- Firefox 3.5 支持跨域 XMLHttpRequests 与 Web Fonts,较旧版本上某些请求会有限制。 Firefox 7 支持 WebGL 纹理的跨域 HTTP 请求,而 Firefox 9 新增支持使用 drawImage 方法,将图形绘制于 canvas 中。

2:服务端CORS支持,两种跨域请求

浏览器将CORS请求分成两类:简单请求(simple request)和预检请求。

同时满足以下条件,那么就是简单请求

(1) 请求方法是以下三种方法之一: HEAD GET POST

(2)HTTP的头信息不超出以下几种字段: Accept Accept-Language Content-Language Last-Event-ID Content-Type:只限于三个值application/x-www-form-urlencoded、multipart/form-data、text/plain

(3)没有事件监听器被注册到任何用来发出请求的 XMLHttpRequestUpload 上(经由 XMLHttpRequest.upload 方法取得)上(4)请求中没有 ReadableStream 类型的内容被用于上传。

通常情况下主要涉及条件(1)和条件(2)

如果不满足上述条件任何一个,那么它就是预检请求。

3:简单请求

浏览器发现自己发送的是简单跨域请求,则会只发送一次HTTP请求。相较于同源请求,CORS简单请求会在头信息中额外增加一个Origin字段。

如果Origin指定的源,不在许可范围内,服务器会返回一个正常的HTTP回应。浏览器发现,这个回应的头信息没有包含Access-Control-Allow-Origin字段,就知道出错了,从而抛出一个错误,被XMLHttpRequest的onerror回调函数捕获。注意,这种错误无法通过状态码识别,因为HTTP回应的状态码有可能是200。

4:预检请求

不满足简单请求条件之一的即是非简单请求。非简单请求的CORS请求,会在正式通信之前,增加一次HTTP查询请求,称为”预检”请求(preflight)

「预检(preflighted)」请求会先用HTTP 的OPTIONS 方法请求另一个域名资源,确认后续实际(actual)请求能否可安全送出。由于跨域请求可能会携带使用者的信息,所以要先进行预检请求。

先看请求,Access-Control-Request-Method告诉服务器发的请求是POST请求,Access-Control-Request-Headers通知自己带有X-PINGOTHER自定义header

再看响应,Access-Control-Allow-Origin这个与前面类似,Access-Control-Allow-Methods这里说明支持POST/GET/OPTIONS方法,Access-Control-Allow-Headers这里说明允许X-PINGOTHER自定义header,Access-Control-Max-Age用来指定本次预检请求的有效时间,86400是24小时也就是一天。

一旦服务器通过了”预检”请求,在Access-Control-Max-Age指定的时间内,以后每次浏览器正常的CORS请求,就都跟简单请求一样,会有一个Origin头信息字段。服务器的回应,也都会有一个Access-Control-Allow-Origin头信息字段。

5:HTTP跨域请求标识

Origin

Origin 字段表示了跨域请求的来源或者预检请求的来源。在任何跨域请求中,一定要携带Origin字段

Access-Control-Request-Method(仅在预检请求中)

Access-Control-Request-Method 是用在预检请求中,告诉后端server实际请求用的HTTP方法

Access-Control-Request-Headers(仅在预检请求中)

Access-Control-Request-Headers标识用于预检请求中,它会告诉后端server自己所携带的自定义header字段有哪些

6:HTTP跨域响应标识

Access-Control-Allow-Origin

跨域响应会携带该字段,若服务器允许所有uri来访问自己的资源,那么则该字段为*;若要允许http://www.qq.com访问该资源,则为Access-Control-Allow-Origin: http://www.qq.com

Access-Control-Expose-Headers

Access-Control-Expose-Headers表示服务器允许浏览器从响应中解析哪些header字段

Access-Control-Max-Age

Access-Control-Max-Age表示预检请求结果请求成功后,多长时间内非简单请求可以不需要再发预检请求,可以继续直接使用跨域请求请求资源

Access-Control-Allow-Methods(仅在预检请求响应中)

Access-Control-Allow-Methods表示服务器访问操作该资源允许哪些方法

Access-Control-Allow-Headers(仅在预检请求响应)

Access-Control-Allow-Headers表示在访问这个域资源的时候,预检请求响应中哪些header字段可以在跨域请求中使用

 

后端代码实现

web.xml配置


<filter>
 <filter-name>corsFilter</filter-name>
 <filter-class>com.web.filter.CorsFilter</filter-class>
</filter>
<filter-mapping>
 <filter-name>corsFilter</filter-name>
 <url-pattern>/api/*</url-pattern>
</filter-mapping>

过滤器filter

package com.web.filter.CorsFilter;
 
import java.io.IOException;
 
import javax.servlet.Filter;
import javax.servlet.FilterChain;
import javax.servlet.FilterConfig;
import javax.servlet.ServletException;
import javax.servlet.ServletRequest;
import javax.servlet.ServletResponse;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
 
import org.springframework.http.HttpHeaders;
 
public class CorsFilter implements Filter {
 
 public static final String ACCESS_CONTROL_REQUEST_METHOD = "Access-Control-Request-Method";
 
 public static final String OPTIONS = "OPTIONS";
 
 public void doFilter(ServletRequest request, ServletResponse response,
  FilterChain chain) throws IOException, ServletException {
 
 HttpServletRequest httpRequest = (HttpServletRequest) request;
 HttpServletResponse httpResponse = (HttpServletResponse) response;
 
 if (isCorsRequest(httpRequest)) {
  httpResponse.setHeader("Access-Control-Allow-Origin", "*");
  httpResponse.setHeader("Access-Control-Allow-Methods",
   "POST, GET, PUT, DELETE");
  httpResponse.setHeader("Access-Control-Allow-Credentials", "true");
  // response.setIntHeader("Access-Control-Max-Age", 1728000);
  httpResponse
   .setHeader(
    "Access-Control-Allow-Headers",
    "Origin, X-Requested-With, Content-Type, Accept, Accept-Encoding, Authorization");
  if (isPreFlightRequest(httpRequest)) {
  return;
  }
 }
 chain.doFilter(request, response);
 }
 
 public void init(FilterConfig filterConfig) {
 }
 
 public void destroy() {
 }
 
 public boolean isCorsRequest(HttpServletRequest request) {
 return (request.getHeader(HttpHeaders.ORIGIN) != null);
 }
 
 /**
 * Returns {@code true} if the request is a valid CORS pre-flight one.
 */
 public boolean isPreFlightRequest(HttpServletRequest request) {
 return (isCorsRequest(request) && OPTIONS.equals(request.getMethod()) && request
  .getHeader(ACCESS_CONTROL_REQUEST_METHOD) != null);
 }
}

 

  • 不同跨域发送优缺点

CORS与JSONP相比,更为先进、方便和可靠。 
1、 JSONP只能实现GET请求,而CORS支持所有类型的HTTP请求。 
2、 使用CORS,开发者可以使用普通的XMLHttpRequest发起请求和获得数据,比起JSONP有更好的错误处理。 
3、 JSONP主要被老的浏览器支持,它们往往不支持CORS,而绝大多数现代浏览器都已经支持了CORS。 
对一个简单的请求,没有自定义头部,要么使用GET,要么使用POST,它的主体是text/plain,请求用一个名叫Orgin的额外的头部发送。Origin头部包含请求页面的头部(协议,域名,端口),这样服务器可以很容易的决定它是否应该提供响应。 
服务器端对于CORS的支持,主要就是通过设置Access-Control-Allow-Origin来进行的。 
Header set Access-Control-Allow-Origin * 
为了防止XSS攻击我们的服务器, 我们可以限制域,比如 
Access-Control-Allow-Origin: http://beyondLi.com

  • 实现跨域应当注意什么

注意nginx时也需要进行跨域设置,前端设置跨域设置,预检测是发送request-method为options。直接返回允许跨域,允许跨域的域名和跨域的方法

nginx配置

nginx添加头

add_header 'Access-Control-Allow-Origin' '*';
add_header 'Access-Control-Allow-Methods' 'GET, POST, OPTIONS, PUT, DELETE';
add_header 'Access-Control-Allow-Headers' 'Content-Type';

nginx对OPTIONS方法,在nginx上将OPTIONS方法返回200,而不是405或403。如果在nginx设置了options的返回,也就不会有filter的什么事情了。

if ($request_method = 'OPTIONS') {
    add_header 'Access-Control-Allow-Origin' '*';
    add_header 'Access-Control-Allow-Methods' 'GET, POST, OPTIONS, PUT, DELETE';
    add_header 'Access-Control-Allow-Headers' 'Content-Type';
    return 200;
}
或
add_header 'Access-Control-Allow-Origin' '*';
add_header 'Access-Control-Allow-Methods' 'GET, POST, OPTIONS, PUT, DELETE';
add_header 'Access-Control-Allow-Headers' 'Content-Type';
#在location处添加以下内容
if ($request_method = 'OPTIONS') {
    return 200;
}

ajax设置xhrFields和crossDomain

$.ajaxSetup({
	global : true,// 默认就是true ,触发全局事件
	type : "POST",
	contentType : "application/x-www-form-urlencoded;charset=utf-8",
	timeout : 10000,
	dataType : 'json',
	xhrFields:{
		withCredentials:true
	},
	crossDomain:true,
	statusCode : {
		310 :function(){//未登录
			//layer.msg("未登录");
			location.href = publicContextPath + "/wechat/oauth2.do?redirectUrl=" + encodeURIComponent(location.href);
		},
		404 : function() {
		  layer.msg("请求路径错误");
		},
		500 : function() {
			  layer.msg("服务器出了点问题");
		}
	},
	// 同步设置
	//async : false
});

 

  • 2
    点赞
  • 19
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值