关于前后端开发中跨域问题(access to xmlhttprequest....has been blocked by policy..)

以下资料查找于跨域的解决办法什么是跨域

一、同源策略(以下介绍来自百度百科)

在这里插入图片描述
简单理解就是两个URL的协议、域名、端口号都相同,就称这两个URL同源。否则就是跨域

二、什么是跨域(CORS全称Cross-Origin Resource Sharing,意为跨域资源共享)

广义的跨域:
1.) 资源跳转: A链接、重定向、表单提交。

2.) 资源嵌入:dom标签中的链接、图片、脚本,还有样式中background:url()、@font-face()等文件外链

3.) 脚本请求: js发起的ajax请求、dom和js对象的跨域操作等

我们通常所说的跨域是狭义的,是由浏览器同源策略限制的一类请求场景。
主要因为上文描述的同源策略限制了 以下几种行为:
1.) Cookie、LocalStorage 和 IndexDB 无法读取
2.) DOM 和 Js对象无法获得
3.) AJAX 请求不能发送
所以可以理解同源策略是产生跨域的问题的罪魁祸首:)

三、请求的分类(简单请求和复杂请求)

1、简单请求
简单请求必须要同时满足下面三个条件:
1)请求方式只能是:GET、POST、HEAD
2)HTTP请求头限制这几种字段:Accept、Accept-Language、Content-Language、Content-Type、Last-Event-ID
3)Content-type只能取:application/x-www-form-urlencoded、multipart/form-data、text/plain
2、复杂请求
不满足简单请求的条件,那么就是复杂请求。
复杂请求会在正式请求发送之前,先发一个预检请求进行校验,校验通过后才能进行正式请求。
例如:
浏览器现在要发送一个put的复杂请求,那么在put请求发送之前,浏览器先发送一个options请求。
options请求头信息(会隐藏一些具体头信息, 只有正常访问时才显示):

OPTIONS /cors HTTP/1.1
Origin: localhost:3000
Access-Control-Request-Method: PUT // 表示使用的什么HTTP请求方法
Access-Control-Request-Headers: X-Custom-Header // 表示浏览器发送的自定义字段
Host: localhost:3000
Accept-Language: zh-CN,zh;q=0.9
Connection: keep-alive
User-Agent: Mozilla/5.0...

服务器收到options请求以后,检查了Origin、Access-Control-Request-Method和Access-Control-Request-Headers字段以后,确认允许跨源请求,就可以做出回应
options响应头信息

HTTP/1.1 200 OK
Date: Mon, 01 Dec 2008 01:15:39 GMT
Server: Apache/2.0.61 (Unix)
Access-Control-Allow-Origin: http://localhost:3000 // 表示http://localhost:3000可以访问数据
Access-Control-Allow-Methods: GET, POST, PUT      
Access-Control-Allow-Headers: X-Custom-Header    
Content-Type: text/html; charset=utf-8
Content-Encoding: gzip
Content-Length: 0
Keep-Alive: timeout=2, max=100
Connection: Keep-Alive
Content-Type: text/plain

当options请求通过之后发出正式的HTTP请求,倘若options请求不通过,则服务器不允许此次访问,从而抛出错误
options请求通过之后的,浏览器发出发请求

PUT /cors HTTP/1.1
Origin: http://api.zhenai.com
Host: api.alice.com
X-Custom-Header: value
Accept-Language: en-US
Connection: keep-alive
User-Agent: Mozilla/5.0...

options请求缓存
在options请求中,我们可以通过设置响应头的参数Access-Control-Max-Age来对结果进行缓存
比如: Access-Control-Max-Age: 600 表示对options检验结果进行十分钟的缓存

url变化会导致缓存失效,需要重新验证options请求的返回值
预检不关心post data
header变化,如果是去掉了自定义的header使得请求变成简单请求,不会发送options请求。如果是增加其他的header,是会重新验证Access-Control-Allow-Headers的值。
cookie变化,只要后端允许发送cookie,cookie值变化不会导致缓存失效。

四、跨域的解决方式(个人自己试过前三种)

1、通过jsonp跨域(注意注意 这种方式只对get请求有效)
1)原生JS实现:

 <script>
    var script = document.createElement('script');
    script.type = 'text/javascript';    // 传参并指定回调执行函数为onBack
    script.src = 'http://www.domain2.com:8080/login?user=admin&callback=onBack';    			 	    document.head.appendChild(script);    // 回调执行函数
    function onBack(res) {
        alert(JSON.stringify(res));
    } </script>

2)jquery ajax:

$.ajax({   
 url: 'http://www.domain2.com:8080/login',   
 type: 'get',   
 dataType: 'jsonp',  // 请求方式为jsonp
 jsonpCallback: "onBack",    // 自定义回调函数名
 data: {}
});

3)vue.js:

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

2、nginx代理跨域
1) nginx配置解决iconfont跨域
浏览器跨域访问js、css、img等常规静态资源被同源策略许可,但iconfont字体文件(eot|otf|ttf|woff|svg)例外,此时可在nginx的静态资源服务器中加入以下配置。
location / {
add_header Access-Control-Allow-Origin *;
}
2、 nginx反向代理接口跨域
跨域原理: 同源策略是浏览器的安全策略,不是HTTP协议的一部分。服务器端调用HTTP接口只是使用HTTP协议,不会执行JS脚本,不需要同源策略,也就不存在跨越问题。
实现思路:通过nginx配置一个代理服务器(域名与domain1相同,端口不同)做跳板机,反向代理访问domain2接口,并且可以顺便修改cookie中domain信息,方便当前域cookie写入,实现跨域登录。
nginx具体配置:

#proxy服务器
server {    
listen       xxxx;    
server_name  www.xxxx.com;
    location / {
     proxy_pass http://localhost:3001;

        #指定允许跨域的方法,*代表所有
        add_header Access-Control-Allow-Methods *;

        #预检命令的缓存,如果不缓存每次会发送两次请求
        add_header Access-Control-Max-Age 3600;
        #带cookie请求需要加上这个字段,并设置为true
        add_header Access-Control-Allow-Credentials true;

        #表示允许这个域跨域调用(客户端发送请求的域名和端口) 
        #$http_origin动态获取请求客户端请求的域   不用*的原因是带cookie的请求不支持*号
        add_header Access-Control-Allow-Origin $http_origin;

        #表示请求头的字段 动态获取
        add_header Access-Control-Allow-Headers 
        $http_access_control_request_headers;

        #OPTIONS预检命令,预检命令通过时才发送请求
        #检查请求的类型是不是预检命令
        if ($request_method = OPTIONS){
            return 200;
        }

    }
}

3、jquery的ajax和java后台
jquery

$.ajax({   
 url: 'http://www.domain2.com:8080/login',   
 type: 'get',   
 dataType: 'jsonp',  // 请求方式为jsonp
 jsonpCallback: "onBack",    // 自定义回调函数名
 data: {}
});

java后端代码在controller请求中加入如下代码


// 允许跨域访问的域名:若有端口需写全(协议+域名+端口),若没有端口末尾不用加'/'
response.setHeader("Access-Control-Allow-Origin", "*");
//* 代办允许所有方法
response.setHeader("Access-Control-Allow-Methods", "*");
// Access-Control-Max-Age 用于 CORS 相关配置的缓存
response.setHeader("Access-Control-Max-Age", "3600");
// 提示OPTIONS预检时,后端需要设置的两个常用自定义头
response.setHeader("Access-Control-Allow-Headers", "*");
// 允许前端带认证cookie:启用此项后,上面的域名不能为'*',必须指定具体的域名,否则浏览器会提示
response.setHeader("Access-Control-Allow-Credentials", "true");

4、WebSocket协议跨域
WebSocket protocol是HTML5一种新的协议。它实现了浏览器与服务器全双工通信,同时允许跨域通讯,是server push技术的一种很好的实现。
原生WebSocket API使用起来不太方便,我们使用Socket.io,它很好地封装了webSocket接口,提供了更简单、灵活的接口,也对不支持webSocket的浏览器提供了向下兼容。

<div>user input:<input type="text"></div>
<script src="./socket.io.js"></script>
<script>
	var socket = io('http://www.domain2.com:8080');// 连接成功处理socket.on('connect', function() {    // 监听服务端消息
    socket.on('message', function(msg) {        console.log('data from server: ---> ' + msg); 
    });    // 监听服务端关闭
    socket.on('disconnect', function() { 
        console.log('Server socket has closed.'); 
    });
});document.getElementsByTagName('input')[0].onblur = function() {
    socket.send(this.value);
};
</script>

5、vue proxyTable
其实,在我们主流使用的MVVM框架中,配置项里面也提供解决跨域问题的能力,继续举个 ,以vue2.x为例,我们可以通过在config/index.js中添加配置项实现跨域请求:

proxyTable: {
    '/apis': {
        // 测试环境
        target: 'http://www.zhenai.com/',  // 接口域名
        changeOrigin: true,  //是否跨域
        pathRewrite: {
            '^/apis': ''   //需要rewrite重写的,
        } 
    }             
}

6、springboot的解决方式
1)在目标方法中加@CrossOrigin注解

@GetMapping("/list")
@CrossOrigin
public List<String> list(){
    List<String> list = Arrays.asList("Java");
    return list;
}

2)添加一个CORS的过滤器

@Configuration
public class CorsConfig {
    
    @Bean
    public CorsFilter corsFilter(){
        CorsConfiguration corsConfiguration = new CorsConfiguration();
        corsConfiguration.addAllowedOrigin("*");
        corsConfiguration.addAllowedHeader("*");
        corsConfiguration.addAllowedMethod("*");
        UrlBasedCorsConfigurationSource source = new UrlBasedCorsConfigurationSource();
        source.registerCorsConfiguration("/**", corsConfiguration);
        return new CorsFilter(source);
    }
    
}

3)第三种:实现 WebMvcConfigurer 接口,重写 addCorsMappings 方法
如果同时使用了spring security需要在security的配置文件中修改,否则不生效

@Configuration
public class CorsConfiguration implements WebMvcConfigurer {

    @Override
    public void addCorsMappings(CorsRegistry registry) {
        registry.addMapping("/**")
                .allowedOriginPatterns("*")//允许哪些域访问
                .allowedMethods("GET","POST","PUT","DELETE","HEAD","OPTIONS")//允许哪些方法访问
                .allowCredentials(true)//是否允许携带cookie
                .maxAge(3600)//设置浏览器询问的有效期
                .allowedHeaders("*");//
    }
}

}

spring security情况下


@Configuration
public class WebSecurityConfig extends WebSecurityConfigurerAdapter {
    @Override
    protected void configure(HttpSecurity http) throws Exception {
        http.authorizeRequests()
                .mvcMatchers("/hello1").permitAll()
                .anyRequest().authenticated()
                .and()
                .formLogin()
                // 跨域配置
                .and()
                .cors()
                .configurationSource(configurationSource());
    }

    CorsConfigurationSource configurationSource() {
        CorsConfiguration corsConfiguration = new CorsConfiguration();
        corsConfiguration.setAllowedHeaders(Collections.singletonList("*"));
        corsConfiguration.setAllowedMethods(Collections.singletonList("*"));
        corsConfiguration.setAllowedOrigins(Collections.singletonList("*"));
        corsConfiguration.setMaxAge(3600L);

        UrlBasedCorsConfigurationSource source = new UrlBasedCorsConfigurationSource();
        source.registerCorsConfiguration("/**", corsConfiguration);
        return source;
    }
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值