跨域问题

时间:2022-06-17
本文章向大家介绍跨域问题,主要内容包括其使用实例、应用技巧、基本知识点总结和需要注意事项,具有一定的参考价值,需要的朋友可以参考一下。

什么是跨域问题

同源策略:

同源指的是域名(或IP),协议,端口都相同,不同源的客户端脚本(javascript、ActionScript)在没明确授权的情况下,不能读写对方的资源。

URL									  解释          是否跨域
http://www.morethink.cn	        原来的URL
http://www.image.morethink.cn	  子域名         跨域(cookie也无法访问)
http://morethink.cn	            不加www        跨域
https://www.morethink.cn	     更改协议        跨域
http://www.morethink.cn:8080	  更改端口号      跨域

原因:

同源政策的目的,是为了保证用户信息的安全,防止恶意的网站窃取数据。
设想这样一种情况:A网站是一家银行,用户登录以后,又去浏览其他网站。如果其他网站可以读取A网站的Cookie,会发生什么?
很显然,如果Cookie包含隐私(比如存款总额),这些信息就会泄漏。更可怕的是,Cookie往往用来保存用户的登录状态,如果用户没有退出登录,其他网站就可以冒充用户,为所欲为。因为浏览器同时还规定,提交表单不受同源政策的限制。
由此可见,"同源政策"是必需的,否则 Cookie 可以共享,互联网就毫无安全可言了。

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

Cookie、LocalStorage 和 IndexDB 无法读取
DOM 和 Js对象无法获得
AJAX 请求不能发送

模拟跨域问题

测试URL为 http://localhost:80/home/allProductions 可以直接在浏览器console中执行

var xhr = new XMLHttpRequest();
xhr.open('GET', 'http://localhost:80/home/allProductions',true);
xhr.send();
xhr.onreadystatechange=function() {
    if(xhr.readyState == 4) {
        if(xhr.status == 200) {
          console.log(JSON.parse(xhr.responseText));
        }
    }
}

在任意网站打开控制台,执行此段代码可以模拟跨域请求。

Mixed Content: The page at 'https://www.zhihu.com/question/26376773' was loaded over HTTPS, but requested an insecure XMLHttpRequest endpoint 'http://localhost/home/allProductions'. This request has been blocked; the content must be served over HTTPS.

模拟跨域请求 模拟跨域请求 再澄清一下跨域问题:

并非浏览器限制了发起跨站请求,而是跨站请求可以正常发起,但是返回结果被浏览器拦截了。最好的例子是CRSF跨站攻击原理,无论是否跨域,请求已经发送到了后端服务器!
但是,有些浏览器不允许从HTTPS的域跨域访问HTTP,比如Chrome和Firefox,这些浏览器在请求还未发出的时候就会拦截请求,这是一个特例。

在控制台打开报错如下

XMLHttpRequest cannot load http://localhost/home/allProductions. No 'Access-Control-Allow-Origin' header is present on the requested resource. Origin 'http://www.cnblogs.com' is therefore not allowed access.

##怎么解决跨域问题

解决方案有很多

通过jsonp跨域 document.domain + iframe跨域 location.hash + iframe window.name + iframe跨域 postMessage跨域 跨域资源共享(CORS) 前端通过Nginx解决跨域问题 nodejs中间件代理跨域 WebSocket协议跨域 这里主要介绍SpringMVC解决跨域问题的方式。

JSONP CORS

JSONP 原理

我虽然请求不了json数据,但是我可以请求一个Content-Type为application/javascript的JavaScript对象,这样就可以避免浏览器的同源策略。

就是当服务器接受到名为jsonp或者callback的参数时,返回Content-Type: application/javascript的结果,从而避免浏览器的同源策略检测。

在控制台中直接进行测试你的jsonp是否配置成功:

function println(data) {
    console.log(data);
}

var url = "http://localhost:80/home/allProductions?&callback=println";
// 创建script标签,设置其属性
var script = document.createElement('script');
script.setAttribute('src', url);
// 把script标签加入head,此时调用开始
document.getElementsByTagName('head')[0].appendChild(script);
jsonp success
jsonp success

使用JQuery测试你的jsonp是否配置成功,因为控制台不能直接加载JQuery,需要自己建立html文件来进行测试:

示例:

$.ajax({
        url: 'http://192.168.0.25:8080/znywLock/assets/queryAssetsList',
        type: 'post',
        async: false,
        data: {'username':'xiaoming','password':'123456'},
        jsonp: "callback",
        dataType: 'jsonp',
        success: function(data) {
            success(data.data)
        },
        error: function(data) {
            $.messager.alert('提示', '出现未知错误!', 'warning')
        }
    })

接下来,java端接收参数处理

@RequestMapping(value="/queryAssetsList",produces = "application/json; charset=utf-8")
@ResponseBody
	public String queryAssetsList(String username,String password,String callback){ //这里的callback一定要和ajax里jsonp值一样
	JSONObject jsonObject=assetsService.queryAssetsList(username,password);
	String str=jsonObject.toString();
	return callback+"("+str+")"; //这里的callback一定要和ajax里jsonp值一样

}

cors

CORS是一个W3C标准,全称是"跨域资源共享"(Cross-origin resource sharing)。 它允许浏览器向跨源服务器,发出XMLHttpRequest请求,从而克服了AJAX只能同源使用的限制。

CORS需要浏览器和服务器同时支持。
所有浏览器都支持该功能,IE浏览器不能低于IE10。
整个CORS通信过程,都是浏览器自动完成,不需要用户参与。 对于开发者来说,CORS通信与同源的AJAX通信没有差别,代码完全一样。浏览器一旦发现AJAX请求跨源,就会自动添加一些附加的头信息,有时还会多出一次附加的请求,但用户不会有感觉。
实现CORS通信的关键是服务器。只要服务器实现了CORS接口,就可以跨源通信。
即CORS与普通请求代码一样。
CORS与JSONP相比
JSONP只能实现GET请求,而CORS支持所有类型的HTTP请求。
使用CORS,开发者可以使用普通的XMLHttpRequest发起请求和获得数据,比起JSONP有更好的错误处理。
JSONP主要被老的浏览器支持,它们往往不支持CORS,而绝大多数现代浏览器都已经支持了CORS。
@CrossOrigin注解
此注解既可用于方法也可用于类

源码如下:

@CrossOrigin(origins = "http://www.zhihu.com")
@RequestMapping(value = "/allProductions", method = RequestMethod.GET)
public Result getAllOldProductions() {

}
@CrossOrigin注解既可注解在方法上,也可注解在类上。

完成配置之后
XML全局配置
所有跨域请求都可以访问

<mvc:cors>
    <mvc:mapping path="/**" />
</mvc:cors>

更加细粒度的配置:

<mvc:cors>
    <mvc:mapping path="/api/**"
        allowed-origins="http://domain1.com, http://domain2.com"
        allowed-methods="GET, PUT"
        allowed-headers="header1, header2, header3"
        exposed-headers="header1, header2" allow-credentials="false"
        max-age="123" />
    <mvc:mapping path="/resources/**"
        allowed-origins="http://domain1.com" />
</mvc:cors>

Spring Boot 配置 CORS

1、使用@CrossOrigin 注解实现

如果想要对某一接口配置 CORS,可以在方法上添加 @CrossOrigin 注解 :

@CrossOrigin(origins = {"http://localhost:9000", "null"})
@RequestMapping(value = "/test", method = RequestMethod.GET)
public String greetings() {
    return "{"project":"just a test"}";
}

如果想对一系列接口添加 CORS 配置,可以在类上添加注解,对该类声明所有接口都有效:

@CrossOrigin(origins = {"http://localhost:9000", "null"})
@RestController
@SpringBootApplication
public class SpringBootCorsTestApplication {
    
}

如果想添加全局配置,则需要添加一个配置类 :

@Configuration
public class WebMvcConfig extends WebMvcConfigurerAdapter {

    @Override
    public void addCorsMappings(CorsRegistry registry) {
        registry.addMapping("/**")
                .allowedOrigins("*")
                .allowedMethods("POST", "GET", "PUT", "OPTIONS", "DELETE")
                .maxAge(3600)
                .allowCredentials(true);
    }
}

另外,还可以通过添加 Filter 的方式,配置 CORS 规则,并手动指定对哪些接口有效。

@Bean
public FilterRegistrationBean corsFilter() {
    UrlBasedCorsConfigurationSource source = new UrlBasedCorsConfigurationSource();
    CorsConfiguration config = new CorsConfiguration();
    config.setAllowCredentials(true);   config.addAllowedOrigin("http://localhost:9000");
    config.addAllowedOrigin("null");
    config.addAllowedHeader("*");
    config.addAllowedMethod("*");
    source.registerCorsConfiguration("/**", config); // CORS 配置对所有接口都有效
    FilterRegistrationBean bean = newFilterRegistrationBean(new CorsFilter(source));
    bean.setOrder(0);
    return bean;
}

原理剖析

无论是通过哪种方式配置 CORS,其实都是在构造 CorsConfiguration。 一个 CORS 配置用一个 CorsConfiguration类来表示,它的定义如下:

public class CorsConfiguration {
    private List<String> allowedOrigins;
    private List<String> allowedMethods;
    private List<String> allowedHeaders;
    private List<String> exposedHeaders;
    private Boolean allowCredentials;
    private Long maxAge;
}
Spring 中对 CORS 规则的校验,都是通过委托给 DefaultCorsProcessor实现的。

DefaultCorsProcessor 处理过程如下:
判断依据是 Header中是否包含 Origin。如果包含则说明为 CORS请求,转到 2;否则,说明不是 CORS 请求,不作任何处理。
判断 response 的 Header 是否已经包含 Access-Control-Allow-Origin,如果包含,证明已经被处理过了, 转到 3,否则不再处理。
判断是否同源,如果是则转交给负责该请求的类处理
是否配置了 CORS 规则,如果没有配置,且是预检请求,则拒绝该请求,如果没有配置,且不是预检请求,则交给负责该请求的类处理。如果配置了,则对该请求进行校验。
校验就是根据 CorsConfiguration 这个类的配置进行判断:
判断 origin 是否合法
判断 method 是否合法
判断 header是否合法
如果全部合法,则在 response header中添加响应的字段,并交给负责该请求的类处理,如果不合法,则拒绝该请求。