• Nginx跨域设置


     一、 跨域概述

    1.1 同源策略

      同源策略是一个安全策略,同源指的是协议、域名、端口相同。浏览器处于安全方面的考虑,只允许本域名下的接口交互,不同源的客户端脚本,在没有明确授权的情况下,不能读写对方的资源。

    同源策略主要是基于如下可能的安全隐患:

    1. 用户访问www.mybank.com,登录并进行网银操作,这时cookie等资源都生成并存放在浏览器;
    2. 用户突然访问另一个网站;
    3. 该网站在页面中,拿到银行的cookie,比如用户名,登录token等,然后发起对www.mybank.com的操作;
    4. 若此时浏览器不对跨域做限制,并且银行也没有做响应的安全处理的话,那么用户的信息有可能就这么泄露了。

    1.2 跨域简介

      CORS是一个W3C标准,全称是跨域资源共享(Cross-origin resource sharing)。即从一个域名的网页去请求另一个域名的资源。本质上对于此类请求,只要协议、域名、端口有任何一个的不同,就被当作是跨域,即都被当成不同源。

      通常基于安全考虑,Nginx启用了同源策略,即限制了从同一个源加载的文档或脚本如何与来自另一个源的资源进行交互。这是一个用于隔离潜在恶意文件的重要安全机制。

    但若同一个公司内部存在多个不同的子域,子域之间需要互访,此时可通过跨域进行实现。跨域可通过JSONP和CORS进行实现。

    注意:

    1. 如果是协议和端口造成的跨域问题"前端"是无法解决的;
    2. 在跨域实现上,仅仅是通过"URL的首部"来识别而不会根据域名对应的IP地址是否相同来判断。"URL的首部"可以理解为"协议,域名和端口必须匹配";
    3. 请求跨域并不是请求发不出去,请求可正常发出,服务端能收到请求并正常返回结果,只是结果被浏览器拦截了。

    提示:本实验基于Nginx的CORS实现跨域,更多JSONP等参考:https://juejin.im/post/5e6c58b06fb9a07ce01a4199。

    1.3 跨域处理流程

    clipboard

    1. 首先查看http头部有无origin字段;
    2. 如果没有,或者不允许,直接当成普通请求处理,结束;
    3. 如果有并且是允许的,那么再看是否是preflight(method=OPTIONS);
    4. 如果是preflight,就返回Allow-Headers、Allow-Methods等,内容为空;
    5. 如果不是preflight,就返回Allow-Origin、Allow-Credentials等,并返回正常内容。
      1 location /pub/(.+) {
      2     if ($http_origin ~ <允许的域(正则匹配)>) {
      3         add_header 'Access-Control-Allow-Origin' "$http_origin";
      4         add_header 'Access-Control-Allow-Credentials' "true";
      5         if ($request_method = "OPTIONS") {
      6             add_header 'Access-Control-Max-Age' 86400;
      7             add_header 'Access-Control-Allow-Methods' 'GET, POST, OPTIONS, DELETE';
      8             add_header 'Access-Control-Allow-Headers' 'reqid, nid, host, x-real-ip, x-forwarded-ip, event-type, event-id, accept, content-type';
      9             add_header 'Content-Length' 0;
     10             add_header 'Content-Type' 'text/plain, charset=utf-8';
     11             return 204;
     12         }
     13     }
     14     # 正常nginx配置
     15     ......
     16 }

    二 CORS介绍

    2.1 CORS实现

    CORS需要浏览器和后端同时支持。在后端配置了CORS实现跨域后,浏览器会自动进行CORS通信,从而实现跨域。

    2.2 请求类型

    在使用CORS的场景下,对于客户端(前端)的常见请求,可分类如下两类请求:

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

      方法:GET、HEAD、POST。

      内容:Content-Type 的值仅限于下列三者之一 :

      • text/plain;
      • multipart/form-data;
      • application/x-www-form-urlencoded 请求中的任意 XMLHttpRequestUpload 对象均没有注册任何事件监听器。
    • 复杂请求

      方法:DELETE、PUT。

      不符合以上条件的请求就肯定是复杂请求了。复杂请求的CORS请求,会在正式通信之前增加一次HTTP查询请求,称为"预检"请求。该请求是option方法的,通过该请求来获知服务端是否允许跨域请求。

    三 Nginx跨域配置

    3.1 配置语法

      语法:add_header name value [always];

      默认值:——

      可配置段:http, server, location, if in location

      配置项释义:

    • Access-Control-Allow-Origin:配置 为 * 表示服务器可以接受所有的请求源(Origin),即接受所有跨域的请求,也可以指定一个确定的URL。
    • Access-Control-Allow-Headers:配置 Access-Control-Allow-Headers代表允许在请求该地址的时候带上指定的请求头,例如:Content-Type,Authorization,使用逗号(,)拼接起来放在双引号(")中,可根据实际请求类型添加,可防止出现以下错误:Request header field Content-Type is not allowed by Access-Control-Allow-Headers in preflight response。这个错误表示当前请求Content-Type的值不被支持。其实是因为发起了"application/json"的类型请求导致的。
    • Access-Control-Allow-Methods:配置 Access-Control-Allow-Methods,代表允许使用指定的方法请求该地址,常见的方法有:GET, POST, OPTIONS, PUT, PATCH, DELETE, HEAD。可防止出现以下错误:Content-Type is not allowed by Access-Control-Allow-Headers in preflight response.
    • Access-Control-Max-Age:配置 Access-Control-Max-Age,代表着在 86400 秒之内不用请求该地址的时候 不需要再进行预检请求,也就是跨域缓存。
    • Access-Control-Allow-Credentials 'true':可选字段,为true表示允许发送Cookie。同时,发送时,必须设置XMLHttpRequest.withCredentials为true才有效,请求若服务器不允许浏览器发送,删除该字段即可。
    • return 204:给OPTIONS 添加 204 的返回,为了处理在发送POST请求时Nginx依然拒绝访问的错误,发送"预检请求"时,需要用到方法 OPTIONS,所以服务器需要允许该方法。
    1. 对于简单请求,如GET,只需要在HTTP Response后添加Access-Control-Allow-Origin。
    2. 对于非简单请求,比如POST、PUT、DELETE等,浏览器会分两次应答。第一次preflight(method: OPTIONS),主要验证来源是否合法,并返回允许的Header等。第二次才是真正的HTTP应答。所以服务器必须处理OPTIONS应答。

      注意:如上的 add_header 最后都可以加上了 always,它表示不管返回状态码是多少都会使 add_header 生效,有些时候服务端可能会返回 4XX 的状态码,这时候如果少了 always 会导致 add_header 失效,从而导致浏览器报跨域错误。

    2.2 配置示例

    方案1 *:通配符,全部允许,存在安全隐患(不推荐)。一旦启用本方法,表示任何域名皆可直接跨域请求:

      1     server {
      2         ...
      3         location / {
      4             # 允许 所有头部 所有域 所有方法
      5             add_header 'Access-Control-Allow-Origin' '*';
      6             add_header 'Access-Control-Allow-Headers' '*';
      7             add_header 'Access-Control-Allow-Methods' '*';
      8             # OPTIONS 直接返回204
      9             if ($request_method = 'OPTIONS') {
     10                 return 204;
     11             }
     12         }
     13         ...
     14     }

    方案2:多域名配置(推荐)

    配置多个域名在map中 只有配置过的允许跨域:

      1  map $http_origin $corsHost {
      2         default 0;
      3         "~https://zzzmh.cn" https://zzzmh.cn;
      4         "~https://chrome.zzzmh.cn" https://chrome.zzzmh.cn;
      5         "~https://bz.zzzmh.cn" https://bz.zzzmh.cn;
      6     }
      7     server {
      8         ...
      9         location / {
     10             # 允许 所有头部 所有$corsHost域 所有方法
     11             add_header 'Access-Control-Allow-Origin' $corsHost;
     12             add_header 'Access-Control-Allow-Headers' '*';
     13             add_header 'Access-Control-Allow-Methods' '*';
     14             # OPTIONS 直接返回204
     15             if ($request_method = 'OPTIONS') {
     16                 return 204;
     17             }
     18         }
     19         ...
     20     }

    四、 其他更多示例

    4.1 区分请求跨域一

      1 server
      2 {
      3     listen 80;
      4     server_name multireq01.linuxds.com;
      5     root root   /usr/share/nginx/multireq01;
      6     access_log  /var/log/nginx/multireq01.access.log  main;
      7     error_log   /var/log/nginx/multireq01.error.log  warn;
      8     location /
      9     {
     10         if ($request_method = 'OPTIONS') {
     11             add_header 'Access-Control-Allow-Origin' '*';
     12             add_header 'Access-Control-Allow-Methods' 'GET, POST, OPTIONS';
     13             add_header 'Access-Control-Allow-Headers' 'DNT,X-CustomHeader,Keep-Alive,User-Agent,X-Requested-With,If-Modified-Since,Cache-Control,Content-Type';
     14             add_header 'Access-Control-Max-Age' 1728000;
     15             add_header 'Content-Type' 'text/plain charset=UTF-8';
     16             add_header 'Content-Length' 0;
     17             return 204;
     18         }
     19         if ($request_method = 'POST') {
     20             add_header 'Access-Control-Allow-Origin' '*';
     21             add_header 'Access-Control-Allow-Methods' 'GET, POST, OPTIONS';
     22             add_header 'Access-Control-Allow-Headers' 'DNT,X-CustomHeader,Keep-Alive,User-Agent,X-Requested-With,If-Modified-Since,Cache-Control,Content-Type';
     23         }
     24         if ($request_method = 'GET') {
     25             add_header 'Access-Control-Allow-Origin' '*';
     26             add_header 'Access-Control-Allow-Methods' 'GET, POST, OPTIONS';
     27             add_header 'Access-Control-Allow-Headers' 'DNT,X-CustomHeader,Keep-Alive,User-Agent,X-Requested-With,If-Modified-Since,Cache-Control,Content-Type';
     28         }
     29     }
     30 }

    4.2 区分请求跨域二

      1 server
      2 {
      3     listen 80;
      4     server_name multireq02.linuxds.com;
      5     root root   /usr/share/nginx/multireq02;
      6     access_log  /var/log/nginx/multireq02.access.log  main;
      7     error_log   /var/log/nginx/multireq02.error.log  warn;
      8     location /
      9     {
     10         if ($request_method = 'OPTIONS') {
     11             add_header 'Access-Control-Allow-Origin' 'https://docs.domain.com';
     12             add_header 'Access-Control-Allow-Credentials' 'true';
     13             add_header 'Access-Control-Allow-Methods' 'GET, POST, PUT, DELETE, PATCH, OPTIONS';
     14             add_header 'Access-Control-Allow-Headers' 'DNT,X-Mx-ReqToken,Keep-Alive,User-Agent,X-Requested-With,If-Modified-Since,Cache-Control,Content-Type,Authorization,token';
     15             return 204;
     16         }
     17         if ($request_method = 'POST') {
     18             add_header 'Access-Control-Allow-Origin' 'https://docs.domain.com';
     19             add_header 'Access-Control-Allow-Credentials' 'true';
     20             add_header 'Access-Control-Allow-Methods' 'GET, POST, PUT, DELETE, PATCH, OPTIONS';
     21             add_header 'Access-Control-Allow-Headers' 'DNT,X-Mx-ReqToken,Keep-Alive,User-Agent,X-Requested-With,If-Modified-Since,Cache-Control,Content-Type,Authorization,token';
     22         }
     23         if ($request_method = 'GET') {
     24             add_header 'Access-Control-Allow-Origin' 'https://docs.domain.com';
     25             add_header 'Access-Control-Allow-Credentials' 'true';
     26             add_header 'Access-Control-Allow-Methods' 'GET, POST, PUT, DELETE, PATCH, OPTIONS';
     27             add_header 'Access-Control-Allow-Headers' 'DNT,X-Mx-ReqToken,Keep-Alive,User-Agent,X-Requested-With,If-Modified-Since,Cache-Control,Content-Type,Authorization,token';
     28         }
     29     }
     30 }
     
     
  • 相关阅读:
    Java IO输入输出流 FileWriter 字符流
    Java IO输入输出流File 字节流
    Java List集合和Map集合的综合应用
    表单提交中的重复问题(表单令牌验证)
    php中const与define的区别
    阿里云中获取文件及目录列表的方法
    巧用php中的array_filter()函数去掉多维空值
    文件大小格式化函数
    UTC 通用格式时间 转换为 时间戳,并格式化为2017-01-01 12:00:00
    关于匿名函数的使用,购物车中计算销售税的应用
  • 原文地址:https://www.cnblogs.com/zgxblog/p/14056701.html
Copyright © 2020-2023  润新知