1. 为什么会有跨域

浏览器出于对于浏览网页安全性问题的考虑,引入的同源策略。

什么是同源策略,听着很官方,简单一幅图说明:

<mark>同源指的是URL地址三要素(协议,主机,端口号)必须不变</mark>

协议 主机 端口
http www.baidu.com 80

例如我客户端访问80端口的百度,只要协议,和端口不变访问
http://www.baidu.com:80/aaa/bbb 满足同源策略,访问京东同样如此。

2. 什么是跨域

同样如上图,如果出现了在百度页面可以访问京东的页面,这中间就出现了跨域问题。

这种问题 会引起什么问题呢?

其实在正常的界面访问直接一般不会出现问题,但是如果是涉及到访问一些需要登录后访问的信息时,这样就会存在安全隐患,例如,同样看图

很明显,可以正常访问到界面2的内容,但是如果界面2的内容属于敏感信息呢,我们不需要被访问到!!!

3. DEMO准备

<mark>项目1:端口号8080</mark>

一个页面

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>首页</title>
    <script src="/js/axios.min.js"></script>
    <script src="/js/vue.min.js"></script>
</head>
<body>

<button id="btn" @click.prevent = "click">点击按钮,测试跨域</button>

</body>

<script> new Vue({
      el:"#btn", methods:{
      click(){
      axios.get("http://localhost:80/jd/index"); } } }); </script>
</html>

简单按钮请求:但是请求的是 另外一个服务器的资源

URL地址是:http://localhost:80/jd/index

<mark>项目2:端口号 80</mark>

@RestController
@RequestMapping("/jd")
public class IndexController {
   


    @GetMapping("/index")
    public String index(){
   
        return "index";
    }
}

两个都是基于SpringBoot构建,如果通过项目1的请求,去访问项目2的controller,这个时候就会发生跨域问题。如下:

4. 解决手段

4.1 设置Access-Control-Allow-Origin

修改项目2的controller

@RestController
@RequestMapping("/jd")
public class IndexController {
   


    @GetMapping("/index")
    public String index(HttpServletResponse response){
   
        //运行项目1访问:只需要协议 + 主机 + 端口
        response.addHeader("Access-Control-Allow-Origin","http://localhost:8080");
        //允许所有访问
// response.addHeader("Access-Control-Allow-Origin","*");


        //扩展:
        // 域间请求的最大等待最时间(单位s)
        //1. 表示每10s发送一次域间请求,不足10s就不需要发送域间请求,提高访问效率
// response.addHeader("Access-Control-Max-age","10");
//
// //2.只允许GET请求
// response.addHeader("Access-Control-Allow-Method","GET");
//
// //3.请求头携带xxx参数
// response.addHeader("Access-Control-Allow-Headers","xxx");
        return "index";
    }
}

同样可以使用SpringBoot自带的注解@CrossOrigin进行配置

@GetMapping("/index")
    @CrossOrigin
    public String index(HttpServletResponse response){
   
        return "index";
    }
@Target({
   ElementType.TYPE, ElementType.METHOD})
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface CrossOrigin {
   
    /** @deprecated */
    @Deprecated
    String[] DEFAULT_ORIGINS = new String[]{
   "*"};
    /** @deprecated */
    @Deprecated
    String[] DEFAULT_ALLOWED_HEADERS = new String[]{
   "*"};
    /** @deprecated */
    @Deprecated
    boolean DEFAULT_ALLOW_CREDENTIALS = false;
    /** @deprecated */
    @Deprecated
    long DEFAULT_MAX_AGE = 1800L;

    @AliasFor("origins")
    String[] value() default {
   };

    @AliasFor("value")
    String[] origins() default {
   };

    String[] originPatterns() default {
   };

    String[] allowedHeaders() default {
   };

    String[] exposedHeaders() default {
   };

    RequestMethod[] methods() default {
   };

    String allowCredentials() default "";

    long maxAge() default -1L;
}

具体配置细节可网上查询 CrossOrigin介绍

4.2 借助RestTemplate实现远程调用

项目1:安排RestTemplate

@Configuration
public class RPCConfig {
   

    @Autowired
    private RestTemplateBuilder restTemplateBuilder;

    @Bean
    public RestTemplate restTemplate(){
   
        return restTemplateBuilder.build();
    }
}

我们转换思路:原先从项目1的index.html访问项目2,现在转换成从项目1的html访问到项目1的controller,再从项目1的controller转而访问项目2的controller,将返回数据再次返给项目1的html,看文字不如看张图

同样可以实现跨域访问

4.3 借助Nginx实现反向代理

将我们的项目1的index.html放入nginx静态服务器中,并和nginx处于同一个监听端口

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>首页</title>
    <script src="/js/axios.min.js"></script>
    <script src="/js/vue.min.js"></script>
</head>
<body>

<button id="btn" @click.prevent = "click">点击按钮,测试跨域</button>

</body>
<script> new Vue({
      el:"#btn", methods:{
      click(){
      axios.get("http://localhost:81/jd/index").then(response=>{
      console.log(response.data); }); } } }); </script>
</html>

nginx配置文件


worker_processes  1;

events {
   
    worker_connections  1024;
}


http {
   
    include       mime.types;
    default_type  application/octet-stream;

    sendfile        on;
    keepalive_timeout  65;
    server {
   
        listen       81;
        server_name  localhost;

        #charset koi8-r;

        #access_log  logs/host.access.log  main;


        location /jd {
   
        #反向代理项目2的地址,只要包含/jd开头的都转发到http://localhost:80
			proxy_pass http://localhost:80;
        }

        location / {
   
            root   html;
            index  index.html index.htm;
        }

        #error_page  404              /404.html;

        # redirect server error pages to the static page /50x.html
        #
        error_page   500 502 503 504  /50x.html;
        location = /50x.html {
   
            root   html;
        }

}

实现跨域

这里需要注意的是,nginx必须监听的是,要代理的端口,这样才可以实现跨域。

通过正则表达式修改Nginx映射规则

location /jd {
   
	rewrite ^/jd/(.*)$ /$1 break;
	proxy_pass http://localhost:80;
}

可以做到代理到80的任意URL,如我们将项目2的controller地址改为
http://localhost/jd/index 也可以正常访问

4.4 jsonp技术

jsonp属于比较老的技术了,运用的是一些天然支持跨域的标签特性,进行函数回调的过程。

举个例子:

在项目1 的index.htm,通过天然支持跨域的标签<script>来实现跨域,<script>请求项目2的

@RestController
@RequestMapping("/jd")
public class IndexController {
   


    /** * 方法2:借助restTemplate 远程调用 * @param response * @return */
    @GetMapping("/index")
    public String index(HttpServletResponse response){
   
        return "cross('es-jd-index')";
    }
}

index.html

<script> function cross(data) {
      console.log(data); } </script>

<script src = 'http://localhost:80/jd/index'></script>

来进行函数的回调执行,这种方法现在也不常用,比较常用的是Nginx来实现跨域!!