跨域/解决跨域方法

一、同源策略

同源策略(Same Origin Policy)是一种约定,它是浏览器最核心也是最基本的安全功能。同源策略会阻止一个域的javascrip脚本和另一个域的内容进行交互,是用于隔离潜在恶意文件的关键安全机制;关于这一点我们后面会举例说明。如果缺少了同源策略浏览器的安全使用会受到很大的影响。可以说web是构建在同源策略基础之上的,浏览器只是针对同源策略的一种实现。

1、同源的定义
当一个请求url的协议、域名、端口三者之间任意一个与当前页面url不同即为跨域。

如下表给出了相对http://www.example.com/dir/page.html的同源检测示例:

URL结果原因
http://www.example.com/dir2/other.html成功协议、域名、端口相同
http://www.example.com/dir/inner/another.html成功协议、域名、端口相同
https://www.example.com/secure.html失败协议不同 ( HTTPS )
http://www.example.com:81/dir/etc.html失败端口不同 ( 81 )
http://news.example.com/dir/other.html失败域名不同

同源策略目的就是为了保证用户信息的安全,防止恶意的网站窃取用户数据。如果网页之间不满足“同源”的要求,那么它们之间:

  1. 不能共享Cookie、LocalStorage、IndexDB
  2. 不能获取DOM
  3. AJAX请求不能发送

2、同源策略作用举例
下面举一个例子说明针对接口的请求没有同源策略会怎么样?

cookie大家应该知道,一般用来处理登录等场景,目的是让服务端知道谁发出的这次请求。如果你请求了接口进行登录,服务端验证通过后会在响应头加入Set-Cookie字段,然后下次再发请求的时候,浏览器会自动将cookie附加在HTTP请求的头字段Cookie中,服务端就能知道这个用户已经登录过了。知道这个之后,我们来看场景:

1.某11到了,于是打开了买买买网站www.maimaimai.com,然后登录成功,然后准备一番剁手操作。
2.期间你的好朋友突然给你发了一个你懂得的网站链接www.nidongde.com,一脸yin笑地跟你说:“你懂的”,你毫不犹豫打开了。
3.你饶有兴致地浏览着www.nidongde.com!由于没有同源策略的限制,它向www.maimaimai.com发起了请求!前面我们说过“服务端验证通过后会在响应头加入Set-Cookie字段,然后下次再发请求的时候,浏览器会自动将cookie附加在HTTP请求的头字段Cookie中”,这样一来,这个不法网站就相当于登录了你的账号,可以为所欲为了!如果这不是一个买买买账号,而是你的银行账号,那……
这个过程就是就是CRFS(跨站请求伪造)攻击了!!参见 这里

   显然,同源策略是非常重要的。否则网站站长、广告联盟、流量统计商、xss黑客,随便哪个人都将无障碍的获取私密信息,例如各个网站的Cookie、email的邮件内容、OA页面的内容、QQ空间里设置为隐私的照片等。

3、同源策略的让步
几乎任何时候安全性和便利性都是负相关的,追求安全性的时候肯定会对便利性造成负面影响。同样,同源策略提升了web前端的安全性,但是却牺牲了web扩展上的灵活性。所以,现代浏览器在安全性可可用性之间选择了一个平衡点。即在遵循同源策略的基础上,选择性的为同源策略“开放了后门”。例如img、script、style等变迁都允许跨域引用资源,严格来说这都是不符合同源要求的。只不过这里只是引用这些资源,并不能读取到这些资源的内容。因此浏览器降低了一点点的安全性,缺大大提升了网站布置的灵活性。

二、跨域问题

​ 由于同源策略存在带来的问题就是跨域问题了。

1、跨域访问限制的流程

  1. 同源策略,它是由Netscape提出的一个著名的安全策略。
  2. 所有支持JavaScript 的浏览器都会使用这个策略。
  3. 所谓同源是指,域名,协议,端口相同。
  4. 当一个浏览器的两个tab页中分别打开来 百度和谷歌的页面
  5. 当浏览器的百度tab页执行一个脚本的时候会检查这个脚本是属于哪个页面的,
  6. 即检查是否同源,只有和百度同源的脚本才会被执行。 [1]
  7. 如果非同源,那么在请求数据时,浏览器会在控制台中报一个异常,提示拒绝访问。
  8. 同源策略是浏览器的行为,是为了保护本地数据不被JavaScript代码获取回来的数据污染,因此拦截的是客户端发出的请求回来的数据接收,即请求发送了,服务器响应了,但是无法被浏览器接收。
    在这里插入图片描述

如上图所示,跨域访问限制的作用流程大致如下:

1、 浏览器发送跨域请求
2、 接收response数据
3、 检查响应头
(1)如果响应头中没有允许跨域访问的配置,则不加载,并报出响应异常
(2)如果响应头中有允许跨域访问的设置,正常加载数据

即,同源策略并不是浏览器不让请求发出去、或者后端拒绝返回数据。实际情况是请求正常发出去了,后端也正常相应了,只不过数据到了浏览器后浏览器不去作用加载而是丢弃了。

三、跨域问题的解决
1、JSONP:以后涉及到在说。
2、CORS(Cross-Origin Resource Sharing)跨域资源共享机制
简称为跨域访问,允许 Web 应用服务器进行跨域访问控制,从而使跨域数据传输得以安全进行。CORS 需要浏览器和服务器同时支持。目前,所有浏览器都支持该功能,IE 浏览器要求版本 IE10 或以上。

    整个 CORS 通信过程,都是浏览器自动完成,不需要用户参与。对于开发者来说,CORS 通信与同源的 AJAX 通信没有差别,代码完全一样。浏览器一旦发现 AJAX 请求跨源,就会自动添加一些附加的头信息,有时还会多出一次附加的请求,但用户无感知。

    因此,实现 CORS 通信的关键是服务器。只要服务器实现了 CORS 接口,即可跨源通信。

(1)CORS使用场景

    用户在使用浏览器的情况下会使用到 CORS,因为控制访问权限的是浏览器而非服务器。因此使用其它客户端的时候无需关心任何跨域问题。

    对于我们遇到的问题来说,CORS 的主要应用是实现在浏览器端使用 AJAX 直接访问 COS 的数据或上传、下载数据,而无需通过用户本身的应用服务器中转。

(2)CORS原理:

我们看一眼之前跨域报错的信息:
在这里插入图片描述

浏览器判断后端有没有返回CORS头(Access-Control-Allow-Origin),发现没有就认为后端不允许跨域,即认为返回的数据不可靠。

所以只要后端能够返回浏览器需要的请求头,即可跨域(相应数据不会被同源策略抛弃)。

上面是表面原理,底层原理比较复杂。

浏览器会将ajax请求分为两类,其处理方案略有差异:简单请求、特殊请求。

简单请求
只要同时满足以下两大条件,就属于简单请求。:

(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

当浏览器发现发起的ajax请求是简单请求时,会在请求头中携带一个字段:Origin.

host, origin 和 referer 有什么区别呢?

名称含义
host表示当前请求要被发送的目的地,说白了就是当前请求目标资源的host,仅包括域名和端口号。在任何类型请求中,request都会包含此header信息。
origin表示当前请求资源所在页面的协议和域名, 这个参数一般只存在于CORS跨域请求中,普通请求没有这个header
referer表示当前请求资源所在页面的完整路径:协议+域名+查询参数(注意不包含锚点信息, 所有类型的请求都包含此header

刚才说过,CORS需要客户端和服务端同时支持。上面这个小操作,算是客户端的支持行为(IE10以下不行)。
在这里插入图片描述

Origin中会指出当前请求属于哪个域(协议+域名+端口)。服务会根据这个值决定是否允许其跨域。

如果服务器允许跨域,需要在返回的响应头中携带下面信息(算是服务端的支持):
在这里插入图片描述

  • Access-Control-Allow-Origin:可接受的域,是一个具体域名或者*(代表任意域名)。上图CORS策略配置的允许跨域请求来源是“*”,表示全部域名都允许。
  • Access-Control-Allow-Credentials:是否允许携带cookie,默认情况下,cors不会携带cookie,除非这个值是true

有关cookie:

要想操作cookie,需要满足3个条件:

  • 服务的响应头中需要携带Access-Control-Allow-Credentials并且为true。
  • 浏览器发起ajax需要指定withCredentials 为true
  • 响应头中的Access-Control-Allow-Origin一定不能为*,必须是指定的域名;必须指定明确的、与请求网页一致的域名。
    这样一来,前后端都支持跨域了,那就跨吧。

如果在测试环境中,前端使用保存后端的cookie?
请见前端跨域配置 axios.defaults.withCredentials = true; 无效 这篇博文

注意

(1)测试截图是根据腾讯云手册的设置实例来的,这里

(2)关于web网页的部署,参见 草稿“Linux下的web服务器搭建”。

(3)另外配置web服务器的时候要注意安全组规则的设置;另外可以通过调整COS的跨域策略对比能否成功跨域在请求和相应方面的差异。
3、使用代理
众所周知,服务器之间的通信是不受浏览器同源策略的限制的,所以根本就不存在跨域之说

SpringBoot解决跨域的5种方式

java解决CORS跨域请求的方式
对于CORS的跨域请求,主要有以下几种方式可供选择:

  • 返回新的CorsFilter

  • 重写 WebMvcConfigurer

  • 使用注解 @CrossOrigin

  • 手动设置响应头 (HttpServletResponse)

  • 自定web filter 实现跨域

注意:

  • CorFilter / WebMvConfigurer / @CrossOrigin 需要 SpringMVC 4.2以上版本才支持,对应springBoot 1.3版本以上。
  • 上面前两种方式属于全局 CORS 配置,后两种属于局部 CORS配置。如果使用了局部跨域是会覆盖全局跨域的规则,所以可以通过 @CrossOrigin 注解来进行细粒度更高的跨域资源控制。
  • 其实无论哪种方案,最终目的都是修改响应头,向响应头中添加浏览器所要求的数据,进而实现跨域。
返回新的CorsFilter(全局跨域)

在任意配置类,返回一个 新的 CorsFIlter Bean ,并添加映射路径和具体的CORS配置路径。

@Configuration
public class GlobalCorsConfig {

    @Bean
    public CorsFilter corsFilter() {
        //1. 添加 CORS配置信息
        CorsConfiguration config = new CorsConfiguration();
        //放行哪些原始域
        config.addAllowedOrigin("*");
        //是否发送 Cookie
        config.setAllowCredentials(true);
        //放行哪些请求方式
        config.addAllowedMethod("*");
        //放行哪些原始请求头部信息
        config.addAllowedHeader("*");
        //暴露哪些头部信息
        config.addExposedHeader("*");
        //2. 添加映射路径
        UrlBasedCorsConfigurationSource corsConfigurationSource = new UrlBasedCorsConfigurationSource();
        corsConfigurationSource.registerCorsConfiguration("/**",config);
        //3. 返回新的CorsFilter
        return new CorsFilter(corsConfigurationSource);
    }
}
重写WebMvcConfigurer(全局跨域)
@Configuration
public class CorsConfig implements WebMvcConfigurer {

    @Override
    public void addCorsMappings(CorsRegistry registry) {
        registry.addMapping("/**")
                //是否发送Cookie
                .allowCredentials(true)
                //放行哪些原始域
                .allowedOrigins("*")
                .allowedMethods(new String[]{"GET", "POST", "PUT", "DELETE"})
                .allowedHeaders("*")
                .exposedHeaders("*");
    }
}
使用注解 (局部跨域)

在控制器(类上)上使用注解 @CrossOrigin:,表示该类的所有方法允许跨域

@RestController
@CrossOrigin(origins = "*")
public class HelloController {

    @RequestMapping("/hello")
    public String hello() {
        return "hello world";
    }
}

在方法上使用注解 @CrossOrigin:

  @RequestMapping("/hello")
  @CrossOrigin(origins = "*")
   //@CrossOrigin(value = "http://localhost:8081") //指定具体ip允许跨域
  public String hello() {
        return "hello world";
  }

手动设置响应头(局部跨域)

使用 HttpServletResponse 对象添加响应头(Access-Control-Allow-Origin)来授权原始域,这里 Origin的值也可以设置为 “*”,表示全部放行。

@RequestMapping("/index")
public String index(HttpServletResponse response) {

    response.addHeader("Access-Allow-Control-Origin","*");
    return "index";
}
使用自定义filter实现跨域

首先编写一个过滤器,可以起名字为MyCorsFilter.java

package cn.wideth.aop;

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.HttpServletResponse;
import org.springframework.stereotype.Component;

@Component
public class MyCorsFilter implements Filter {

  public void doFilter(ServletRequest req, ServletResponse res, 
  FilterChain chain) throws IOException, ServletException {
  
    HttpServletResponse response = (HttpServletResponse) res;
    response.setHeader("Access-Control-Allow-Origin", "*");
    response.setHeader("Access-Control-Allow-Methods", "POST, GET, OPTIONS, DELETE");
    response.setHeader("Access-Control-Max-Age", "3600");
    response.setHeader("Access-Control-Allow-Headers", "x-requested-with,content-type");
    chain.doFilter(req, res);
    
  }
  
  public void init(FilterConfig filterConfig) {}
  public void destroy() {}
}

axios写全请求路径并把前端项目发布nginx代理服务器下,nginx配置的代理不生效

浏览器从反向代理服务域名路径下发出的请求到底属于浏览器直接发出的 还是属于浏览器发送请求到代理服务器,然后代理服务器在发送请求到目前服务器 ?

站在http协议上的话,浏览器当前窗口的url和http请求的origin一样。但是站在tcp协议的话,是不是有个“标识”用来标记网络地址,以及目标请求要到哪里?

  • 属于浏览器从当前域名页面发出的,不过一种请求是发送到http://39.170.97.149:8920/DF/common/cockpit/huzhou/getEcologicalBurialSubsidyList。这种被nginx起的server(39.170.97.149下nginx起了一个8920端口的server)接收到(因为nginx这个server只是监听接受请求,并不是监听发出请求的)
Host: 39.170.97.149:8920
Origin: http://39.170.97.149:8920
Pragma: no-cache
Referer: http://39.170.97.149:8920/

//这种被nginx起的server(39.170.97.149下nginx起了一个8920端口的server)接收到了
  • 另一种是 请求网址: https://223.4.73.197/evo-apigw/evo-brm/1.2.0/tree
    Referer: http://localhost:9004/
请求网址: https://223.4.73.197/evo-apigw/evo-brm/1.2.0/tree
Referer: http://39.170.97.149:8920/
  • 因为没有走nginx代理,直接是从浏览器发送到host指定的域,所以跨域了。
    在这里插入图片描述
    请添加图片描述
    请添加图片描述
    请添加图片描述
    请添加图片描述
  • 0
    点赞
  • 8
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值