乐趣区

关于java:Nginx-轻松搞定跨域问题

起源:酒香逢 \
地址:https://www.cnblogs.com/fnz0/…

当你遇到跨域问题,不要立即就抉择复制去尝试,请具体看完这篇文章再解决,我置信它能帮到你。

剖析前筹备:

前端网站地址:http://localhost:8080

服务端网址:http://localhost:59200

首先保障服务端是没有解决跨域的,其次,先用 postman 测试服务端接口是失常的

当网站 8080 去拜访服务端接口时,就产生了跨域问题,那么如何解决?接下来我把跨域遇到的各种状况都列举进去并通过 nginx 代理的形式解决(后盾也是一样的,只有你了解的原理)。

跨域次要波及 4 个响应头:

  • Access-Control-Allow-Origin 用于设置容许跨域申请源地址(预检申请和正式申请在跨域时候都会验证)
  • Access-Control-Allow-Headers 跨域容许携带的非凡头信息字段(只在预检申请验证)
  • Access-Control-Allow-Methods 跨域容许的申请办法或者说 HTTP 动词(只在预检申请验证)
  • Access-Control-Allow-Credentials 是否容许跨域应用 cookies,如果要跨域应用 cookies,能够增加上此申请响应头,值设为 true(设置或者不设置,都不会影响申请发送,只会影响在跨域时候是否要携带 cookies,然而如果设置,预检申请和正式申请都须要设置)。不过不倡议跨域应用(我的项目中用到过,不过不稳固,有些浏览器带不过来),除非必要,因为有很多计划能够代替。

网上很多文章都是通知你间接 Nginx 增加这几个响应头信息就能解决跨域,当然大部分状况是能解决,然而我置信还是有很多状况,明明配置上了,也同样会报跨域问题。

什么是预检申请?:当产生跨域条件时候,览器先询问服务器,以后网页所在的域名是否在服务器的许可名单之中,以及能够应用哪些 HTTP 动词和头信息字段。只有失去必定回答,浏览器才会收回正式的 XMLHttpRequest 申请,否则就报错。如下图

开始入手模仿:

Nginx 代理端口:22222 , 配置如下

server {
    listen       22222;
    server_name  localhost;
    location  / {proxy_pass  http://localhost:59200;}
}

测试代理是否胜利,通过 Nginx 代理端口 2222 再次拜访接口,能够看到如下图通过代理后接口也是能失常拜访

接下来开始用网站 8080 拜访 Nginx 代理后的接口地址,报错状况如下↓↓↓

状况 1:

Access to XMLHttpRequest at ‘http://localhost:22222/api/Lo…’ from origin ‘http://localhost:8080’ has been blocked by CORS policy: Response to preflight request doesn’t pass access control check: No ‘Access-Control-Allow-Origin’ header is present on the requested resource.

通过错误信息能够很清晰的定位到谬误(留神看标红局部)priflight 阐明是个预申请,CORS 机制跨域会首先进行 preflight(一个 OPTIONS 申请),该申请胜利后才会发送真正的申请。这一设计旨在确保服务器对 CORS 规范知情,以爱护不反对 CORS 的旧服务器

通过错误信息,咱们能够失去是预检申请的申请响应头短少了 Access-Control-Allow-Origin,错哪里,咱们改哪里就好了。批改 Nginx 配置信息如下(红色局部为增加局部),缺什么就补什么,很简单明了

server {
    listen       22222;
    server_name  localhost;
    location  / {
       add_header Access-Control-Allow-Origin 'http://localhost:8080';
       proxy_pass  http://localhost:59200;
    }
}

哈哈,当满怀欢喜的认为能解决后,发现还是报了同样的问题

不过咱们的配置没什么问题, 问题在 Nginx, 下图链接 http://nginx.org/en/docs/http…

add_header 指令用于增加返回头字段,当且仅当状态码为图中列出的那些时无效。如果想要每次响应信息都携带头字段信息,须要在最初增加 always(经我测试,只有 Access-Control-Allow-Origin 这个头信息须要加 always,其余的不加 always 也会携带回来),那咱们加上试试

server {
    listen       22222;
    server_name  localhost;
    location  / {
       add_header Access-Control-Allow-Origin 'http://localhost:8080' always;
       proxy_pass  http://localhost:59200;
    }
}

批改了配置后,发现失效了,当然不是跨域就解决了,是下面这个问题曾经解决了,因为报错内容曾经变了

状况 2:

Access to XMLHttpRequest at ‘http://localhost:22222/api/Lo…’ from origin ‘http://localhost:8080’ has been blocked by CORS policy: Response to preflight request doesn’t pass access control check: It does not have HTTP ok status.

通过报错信息提醒能够得悉,是跨域浏览器默认行为的预申请(option 申请)没有收到 ok 状态码,此时再批改配置文件,当申请为 option 申请时候,给浏览器返回一个状态码(个别是 204)

server {
    listen       22222;
    server_name  localhost;
    location  / {
       add_header Access-Control-Allow-Origin 'http://localhost:8080' always;
       if ($request_method = 'OPTIONS') {return 204;}
       proxy_pass  http://localhost:59200;
    }
}

当配置完后,发现报错信息变了

状况 3:

Access to XMLHttpRequest at ‘http://localhost:22222/api/Lo…’ from origin ‘http://localhost:8080’ has been blocked by CORS policy: Request header field authorization is not allowed by Access-Control-Allow-Headers in preflight response.

意思就是预申请响应头 Access-Control-Allow-Headers 中短少头信息 authorization(各种状况会不一样,在产生跨域后,在自定义增加的头信息是不容许的,须要增加到申请响应头 Access-Control-Allow-Headers 中,以便浏览器晓得此头信息的携带是服务器抵赖非法的,我这里携带的是 authorization,其余的可能是 token 之类的,缺什么加什么),晓得了问题所在,而后批改配置文件,增加对应短少的局部,再试试

server {
    listen       22222;
    server_name  localhost;
    location  / {
       add_header Access-Control-Allow-Origin 'http://localhost:8080' always;
       if ($request_method = 'OPTIONS') {
           add_header Access-Control-Allow-Headers 'authorization'; #为什么写在 if 外面而不是接着 Access-Control-Allow-Origin 往下写?因为这里只有预检申请才会查看
           return 204;
      }
    proxy_pass http://localhost:59200;
}
}

此时发现报错问题又回到了状况 1

经测试验证,只有 if ($request_method = ‘OPTIONS’) 外面写了 add_header,当为预检申请时内部配置的都会生效,为什么?↓↓。

官网文档是这样说的:

There could be several add_header directives. These directives are inherited from the previous level if and only if there are no add_header directives defined on the current level.

意思就是以后层级无 add_header 指令时,则继承上一层级的 add_header。相同的若以后层级有了 add_header,就应该无奈继承上一层的 add_header。

配置批改如下:

server {
    listen       22222;
    server_name  localhost;
    location  / {
        add_header Access-Control-Allow-Origin 'http://localhost:8080' always;
        if ($request_method = 'OPTIONS') {
            add_header Access-Control-Allow-Origin 'http://localhost:8080';
            add_header Access-Control-Allow-Headers 'authorization';
            return 204;
        }
        proxy_pass  http://localhost:59200;
    }
}

此时改完发现跨域问题曾经解决了,

不过以上尽管解决了跨域问题,然而思考前期可能 Nginx 版本更新, 不晓得这个规定会不会被批改,思考到这样的写法可能会携带上两个 Access-Control-Allow-Origin,这种状况也是不容许的,上面会说到。所以配置适当批改如下:

server {
    listen       22222;
    server_name  localhost;
    location  / {if ($request_method = 'OPTIONS') {
            add_header Access-Control-Allow-Origin 'http://localhost:8080';
            add_header Access-Control-Allow-Headers 'authorization';
            return 204;
        }
        if ($request_method != 'OPTIONS') {add_header Access-Control-Allow-Origin 'http://localhost:8080' always;}
        proxy_pass  http://localhost:59200;
    }
}

还没完,持续聊 ↓↓

状况 4:

比拟晚期的 API 可能只用到了 POST 和 GET 申请,而 Access-Control-Allow-Methods 这个申请响应头跨域默认只反对 POST 和 GET,当呈现其余申请类型时候,同样会呈现跨域异样。

比方,我这里将申请的 API 接口申请形式从原来的 GET 改成 PUT,在发动一次试试。在管制台上会抛出谬误:

Access to XMLHttpRequest at ‘http://localhost:22222/api/Lo…’ from origin ‘http://localhost:8080’ has been blocked by CORS policy: Method PUT is not allowed by Access-Control-Allow-Methods in preflight response.

报错内容也讲的很分明,在这个预申请中,PUT 办法是不容许在跨域中应用的,咱们须要改下 Access-Control-Allow-Methods 的配置 (缺什么加上么,这里我只加了 PUT,能够本人加全一点),让浏览器晓得服务端是容许的

server {
    listen 22222;
    server_name localhost;
    location / {if ($request_method = 'OPTIONS') {
            add_header Access-Control-Allow-Origin 'http://localhost:8080';
            add_header Access-Control-Allow-Headers 'content-type,authorization';
            add_header Access-Control-Allow-Methods 'PUT';# 为这么只加在这个 if 中,不再上面的 if 也加上?因为这里只有预检申请会校验,当然你加上也没事。return 204;
        }
        if ($request_method != 'OPTIONS') {add_header Access-Control-Allow-Origin 'http://localhost:8080' always;}
        proxy_pass http://localhost:59200;
    }
}

这里留神一下,改成 PUT 类型后,Access-Control-Allow-Headers 申请响应头又会主动校验 content-type 这个申请头,和状况 3 是一样的,缺啥补啥就行了。如果不加上 content-type,则会报如下谬误。(想简略的话,Access-Control-Allow-Headers 和 Access-Control-Allow-Methods 能够设置为 , 示意全都匹配。然而 Access-Control-Allow-Origin 就不倡议设置成 了,为了平安思考,限度域名是很有必要的。)

都加上后,问题就解决了,这里报 405 是我服务端这个接口只凋谢了 GET,没有凋谢 PUT,而此刻我将此接口用 PUT 办法去申请,所以接口会返回这个状态码。

状况 5:

最初再说一种状况,就是后端解决了跨域,就不须要本人在解决了(这里吐槽下,某些后端工程师本人改服务端代码解决跨域,然而又不了解其中原理,网上轻易找段代码黏贴,导致响应信息可能解决不齐全,如 method 没增加全,headers 没加到点上,本人用的那个可能复制过去的并不蕴含理论我的项目所用到的,没有增加 options 申请返回状态码等,导致 Nginx 再用通用的配置就会可能报以下异样)

Access to XMLHttpRequest at ‘http://localhost:22222/api/Lo…’ from origin ‘http://localhost:8080’ has been blocked by CORS policy: The ‘Access-Control-Allow-Origin’ header contains multiple values ‘*, http://localhost:8080’, but only one is allowed.

意思就是此刻 Access-Control-Allow-Origin 申请响应头返回了多个,而只容许有一个,这种状况当然批改配置去掉 Access-Control-Allow-Origin 这个配置就能够了,不过遇到这种状况,倡议 Nginx 配置和服务端本人解决跨域只选其一。(这里留神如果按我下面的写法,if $request_method = ‘OPTIONS’ 这个外面的 Access-Control-Allow-Origin 可不能删除,删除!=’OPTIONS’ 外面的就好了,因为这里如果是预检申请间接就 ruturn 了,申请不会再转发到 59200 服务,如果也删除了,就会报和状况 1 一样的谬误。所以为什么说要不服务端代码层面解决跨域,要不就 Nginx 代理解决,不要混着搞,不然不明确原理的人,网上找一段代码贴就很可能解决不了问题)

↓ ↓ ↓ ↓ ↓

再贴一份残缺配置(* 号依据本人‘爱好’填写):

server {
    listen       22222;
    server_name  localhost;
    location  / {if ($request_method = 'OPTIONS') {
            add_header Access-Control-Allow-Origin 'http://localhost:8080';
            add_header Access-Control-Allow-Headers '*';
            add_header Access-Control-Allow-Methods '*';
            add_header Access-Control-Allow-Credentials 'true';
            return 204;
        }
        if ($request_method != 'OPTIONS') {
            add_header Access-Control-Allow-Origin 'http://localhost:8080' always;
            add_header Access-Control-Allow-Credentials 'true';
        }
        proxy_pass  http://localhost:59200;
    }
}

或者:

server {
    listen       22222;
    server_name  localhost;
    location  / {
        add_header Access-Control-Allow-Origin 'http://localhost:8080' always;
        add_header Access-Control-Allow-Headers '*';
        add_header Access-Control-Allow-Methods '*';
        add_header Access-Control-Allow-Credentials 'true';
        if ($request_method = 'OPTIONS') {return 204;}
        proxy_pass  http://localhost:59200;
    }
}

最初,这是一篇解决跨域遇到问题解决问题的过程,如果认真看完了,我置信应该都能很容易的了解,并且在理论应用中本人解决该问题,心愿能帮忙到大家,以上内容都是本人了解本人测试码进去的,如有了解不对的中央,望大家斧正。

近期热文举荐:

1.1,000+ 道 Java 面试题及答案整顿 (2022 最新版)

2. 劲爆!Java 协程要来了。。。

3.Spring Boot 2.x 教程,太全了!

4. 别再写满屏的爆爆爆炸类了,试试装璜器模式,这才是优雅的形式!!

5.《Java 开发手册(嵩山版)》最新公布,速速下载!

感觉不错,别忘了顺手点赞 + 转发哦!

退出移动版