Nginx分布式框架详解61-71静态资源访问-01

nginx跨域问题的原因分析

跨域问题,我们主要从以下方面进行解决:

  • 什么情况下会出现跨域问题
  • 实例演示跨域问题
  • 具体的解决方案是什么
同源策略

浏览器的同源策略:是一种约定,是浏览器最核心也是最基本的安全功能,如果浏览器少了同源策略,则浏览器的正常功能可能都会受到影响。
同源:协议、域名(ip)、端口相同即为同源

http://192.168.200.131/user/1
https://192.168.200.131/user/1
# 不满足同源

http://192.168.200.131/user/1
http://192.168.200.132/user/1
# 不满足同源

http://192.168.200.131/user/1
http://192.168.200.131:8080/user/1
# 不满足同源

http://www.nginx.com/user/1
http://www.nginx.org/user/1
# 不满足同源

http://192.168.200.131/user/1
http://192.168.200.131:8080/user/1
# 不满足同源

http://www.nginx.org:80/user/1
http://www.nginx.org/user/1
# 满足同源
跨域问题

简单描述下:有两台服务器分别为A、B,如果从服务器A的页面发送异步请求到服务器B获取数据,如果服务器A和服务器B不满足同源策略,则就会出现跨域问题。

nginx跨域问题的案例演示

出现跨域问题会有什么效果?接下来通过一个需求来给大家演示下:

  1. Nginx 的 html 目录下新建一个 a.html
    vim /usr/local/nginx/html/a.html
    添加如下内容:
<html>
  <head>
        <meta charset="utf-8">
        <title>跨域问题演示</title>
        <script src="jquery.js"></script>
        <script>
            $(function(){
                $("#btn").click(function(){
                        $.get('http://mayanan.cn:8080/getUser',function(data){
                                alert(JSON.stringify(data));
                        });
                });
            });
        </script>
  </head>
  <body>
        <input type="button" value="获取数据" id="btn"/>
  </body>
</html>
  1. 在nginx.conf配置如下内容
server {
    listen 80;
    server_name mayanan.cn;
    location / {
        root html;
        index index.html;
    }
}
server {
    listen 8080;
    server_name mayanan.cn;
    location = /getUser {
        default_type application/json;
        return 200 '{"id": 1, "name": "马亚南", "age": 18}';
    }
}
  1. 通过浏览器测试访问
    http://mayanan.cn/a.html
    直接报错:
Access to XMLHttpRequest at 'http://mayanan.cn:8080/getUser' from origin 'http://mayanan.cn' has been blocked by CORS policy: No 'Access-Control-Allow-Origin' header is present on the requested resource.

nginx解决跨域问题的具体实现

使用 add_header 指令,该指令可以用来添加一些头信息。

语法 默认值 位置
add_header ...... http、server、location

此处用来解决跨域问题,需要添加两个头信息,分别是

  • Access-Control-Allow-Origin
  • Access-Control-Allow-Methods
    Access-Control-Allow-Origin:直译过来是允许跨域访问的源地址信息,可以配置多个(多个用逗号分隔),也可以使用 * 代表所有源。
    Access-Control-Allow-Methods:直译过来是允许跨域访问的请求方式,值可以为 GET、POST、PUT、DELETE ......,可以全部设置,也可以根据需要设置,多个用逗号分隔。
    具体配置方式:新增两行
location = /getUser {
    add_header Access-Control-Allow-Origin http://mayanan.cn;  # 新增第一行
    add_header Access-Control-Allow-Methods GET,POST,PUT,DELETE;  # 新增第二行
    default_type application/json;
    return 200 '{"id": 1, "name": "马亚南", "age": 18}';
}

nginx静态资源盗链的效果展示

什么是资源盗链

资源盗链指的是此内容不在自己服务器上,而是通过技术手段,绕过别人的限制将别人的内容放到自己页面上最终展示给用户。以此来盗取大网站的空间和流量。简而言之就是用别人的东西成就自己的网站。

提供两种图片进行演示:

<html>
  <head>
        <meta charset="utf-8">
        <title>跨域问题演示</title>
        <script src="jquery.js"></script>
        <script>
            $(function(){
                $("#btn").click(function(){
                        $.get('http://mayanan.cn:8080/getUser',function(data){
                                alert(JSON.stringify(data));
                        });
                });
            });
        </script>
  </head>
  <body>
        <input type="button" value="获取数据" id="btn"/>
        <img src="https://img14.360buyimg.com/n7/jfs/t1/101062/37/2153/254169/5dcbd410E6d10ba22/4ddbd212be225fcd.jpg"><br>
        <img src="https://pics7.baidu.com/feed/cf1b9d16fdfaaf516f7e2011a7cda1e8f11f7a1a.jpeg?token=551979a23a0995e5e5279b8fa1a48b34&s=BD385394D2E963072FD48543030030BB">
  </body>
</html>

访问http://mayanan.cn/a.html查看效果
从效果图,可以看出来,百度的图片地址添加了防止盗链的功能,京东这边我们可以直接使用其图片。

nginx防盗链的实现原理和实现步骤

了解防盗链的原理之前,我们得先学习一个 HTTP 的头信息 Referer,当浏览器向 Web 服务器发送请求的时候,一般都会带上 Referer,来告诉浏览器该网页是从哪个页面链接过来的。

后台服务器可以根据获取到的这个 Referer 信息来判断是否为自己信任的网站地址,如果是则放行继续访问,如果不是则可以返回 403(服务端拒绝访问)的状态信息。

防盗链实现实例

在本地模拟上述的服务器效果图:

Nginx 防盗链的具体实现:

valid_referers 指令:Nginx 会通过查看 Referer 自动和 valid_referers 的内容进行匹配,如果匹配到了就将 $invalid_referer 变量置 0,如果没有匹配到,则将 $invalid_referer 变量置为 1,匹配的过程中不区分大小写。

所以我们可以在配置文件判断 $invalid_referer 是否等于 1(true),即没有匹配到 ,等于则返回 403。

语法 默认值 位置
valid_referers <none blocked server_names
  • none:如果 Header 中的 Referer 为空,允许访问
  • blocked:在 Header 中的 Referer 不为空,但是该值被防火墙或代理进行伪装过,如不带『 http:// 』 、『 https:// 』等协议头的资源才允许访问。
  • server_names:指定具体的域名或者 IP
  • string:可以支持正则表达式和 * 的字符串。如果是正则表达式,需要以 ~ 开头表示
    例如:
location ~ .*\.(png|jpg|jpeg)$ {
    valid_referers none blocked mayanan.cn *.mayanan.cn;
    if ($invalid_referer) {
        return 403;
    }
}

上方代码如果没有匹配上 mayanan.cn 和 *.mayanan.cn,则 $invalid_referer 为 1(true),返回 403,代表不允许获取资源。
Nginx 配置文件支持 if 判断,但是 if 后面必须有空格。
问题:如果图片有很多,该如何批量进行防盗链?可以针对目录进行防盗链。

针对目录防盗链

假设 html 目录下有一个 images 目录,里面专门放防盗链的图片。
配置如下:

server {
    listen 80;
    server_name mayanan.cn;
    location /images {
        valid_referers none blocked mayanan.cn;
        if ($invalid_referer) {
            return 403;
        }
        root /usr/local/nginx/html;
    }
}

只需将 location 的地址改成一个目录,这样我们可以对一个目录下的所有资源进行了防盗链操作。
问题:Referer 的限制比较粗,比如浏览器发送请求时恶意加一个 Referer,上面的方式是无法进行限制的。那么这个问题改如何解决?
此时我们需要用到 Nginx 的第三方模块 ngx_http_accesskey_module,第三方模块如何实现盗链,如何在 Nginx 中使用第三方模块的功能,在后面有讲解。

nginx的Rewrite内容介绍

Rewrite 是 Nginx 服务器提供的一个重要基本功能,是 Web 服务器产品中几乎必备的功能。主要的作用是用来实现 URL 的重写。

注意:
Nginx 服务器的 Rewrite 功能的实现依赖于 PCRE 的支持,因此在编译安装 Nginx 服务器之前,需要安装 PCRE 库。Nginx 使用的是ngx_http_rewrite_module 模块来解析和处理 Rewrite 功能的相关配置。
地址重写和地址转发

重写和转发的区别:

  • 地址重写浏览器的地址会发生变化而地址转发则不会
  • 一次地址重写会产生两次请求而一次地址转发只会产生一次请求
  • 地址重写的页面必须是一个完整的路径而地址转发则不需要
  • 地址重写因为是两次请求,所以request范围内属性不能传递给新页面,而地址转发因为是一次请求所以可以传递值
  • 地址转发速度快于地址重写

rewrite之set指令

该指令用来设置一个新的变量。

语法 默认值 位置
set <$key> ; server、location、if
  • variable:变量的名称,该变量名称要用 $ 作为变量的第一个字符,且不能与 Nginx 服务器内置的全局变量同名。
  • value:变量的值,可以是字符串、其他变量或者变量的组合等。
    例如:
server {
    listen 80;
    server_name mayanan.cn;
    location / {
        set $name TOM;
        set $age 18;
        default_type text/plain;
        return 200 $name=$age;
    }
}
rewrite常用全局变量
变量 说明
$args 变量中存放了请求 URL 中的请求指令。比如 http://192.168.200.133:8080?arg1=value1&args2=value2 中的『 arg1=value1&arg2=value2 』,功能和 $query_string 一样
$http_user_agent 变量存储的是用户访问服务的代理信息(如果通过浏览器访问,记录的是浏览器的相关版本信息)
$host 变量存储的是访问服务器的 server_name 值
$document_uri 变量存储的是当前访问地址的URI。比如 http://192.168.200.133/server?id=10&name=zhangsan中的『 /server 』,功能和 $uri 一样
$document_root 变量存储的是当前请求对应 location 的 root 值,如果未设置,默认指向 Nginx 自带 html 目录所在位置
$content_length 变量存储的是请求头中的 Content-Length 的值
$content_type 变量存储的是请求头中的 Content-Type 的值
$http_cookie 变量存储的是客户端的 cookie 信息,可以通过 add_header Set-Cookie 'cookieName=cookieValue' 来添加 cookie 数据
$limit_rate 变量中存储的是 Nginx 服务器对网络连接速率的限制,也就是 Nginx 配置中对 limit_rate 指令设置的值,默认是 0,不限制。
$remote_addr 变量中存储的是客户端的 IP 地址
$remote_port 变量中存储了客户端与服务端建立连接的端口号
$remote_user 变量中存储了客户端的用户名,需要有认证模块才能获取
$scheme 变量中存储了访问协议
$server_addr 变量中存储了服务端的地址
$server_name 变量中存储了客户端请求到达的服务器的名称
$server_port 变量中存储了客户端请求到达服务器的端口号
$server_protocol 变量中存储了客户端请求协议的版本,比如 『 HTTP/1.1 』
$request_body_file 变量中存储了发给后端服务器的本地文件资源的名称
$request_method 变量中存储了客户端的请求方式,比如『 GET 』,『 POST 』等
$request_filename 变量中存储了当前请求的资源文件的路径名
$request_uri 变量中存储了当前请求的 URI,并且携带请求参数,比如 http://192.168.200.133/server?id=10&name=zhangsan 中的 『 /server?id=10&name=zhangsan 』

例如:

server {
    listen 8081;
    server_name localhost;
    location /server {
        root /usr/local/nginx/abc;
        set $name TOM;
        set $age 18;
        default_type text/plain;
        return 200 $name=$age=$args=$http_user_agent=$host=$document_root;
    }
}

访问:http://mayanan.cn/server?id=11
返回结果:

TOM=18--Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/104.0.0.0 Safari/537.36--/usr/local/nginx/abc

通过nginx内置全局变量,自定义日志:

# 配置请求处理日志格式
log_format main '$remote_addr - $request - $status - $request_uri - $http_user_agent';

server {
    listen 80;
    server_name mayanan.cn;
    location /server {
        access_log logs/my.log main;
        root /usr/local/nginx/abc;
        set $name TOM;
        set $age 18;
        default_type text/plain;
        return 200 $name=$age--$http_user_agent--$document_root;
    }
}

访问后产生日志结果:http://mayanan.cn/server?id=11

123.112.22.241 - GET /server?id=11 HTTP/1.1 - 200 - /server?id=11 - Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/104.0.0.0 Safari/537.36

rewrite之if指令

该指令用来支持条件判断,并根据条件判断结果选择不同的 Nginx 配置。

语法 默认值 位置
if (condition) server、location

if 和括号之间要有空格,condition 为判定条件,可以支持以下写法:

  • 变量名。如果变量名对应的值为空或者是 0,if 都判断为 false,其他条件为 true。
location /server {
    set $myname 1;
    default_type text/plain;
    if ($myname) {
        return 200 OK;
    }
    return 200 $document_uri--$args--$request_uri;
}
  • 使用『 = 』和『 != 』比较变量和字符串是否相等,满足条件为 true,不满足为 false
    注意:POST 和 Java 不太一样的地方是字符串不需要添加引号。
  • 使用正则表达式对变量进行匹配,匹配成功返回 true,否则返回 false。变量与正则表达式之间使用『 ~ 』,『 ~* 』,『 !~ 』,『 !~* 』来连接。
    『 ~ 』代表匹配正则表达式过程中区分大小写,进行模糊匹配
    『 ~* 』代表匹配正则表达式过程中不区分大小写,进行模糊匹配
    『 !~ 』和『 !~* 』刚好和上面取相反值,如果匹配上返回 false,匹配不上返回 true,进行模糊匹配
location /server {
    default_type text/plain;
    charset utf-8;
    if ($http_user_agent ~ Chrome) {
        return 405 浏览器不允许访问;
    }
    return 200 $document_uri--$args--$request_uri;
}
  • 判断请求的文件是否存在使用『 -f 』和『 !-f 』
    当使用『 -f 』时,如果请求的文件存在返回 true,不存在返回 false。
    当使用『 !-f 』时,如果请求文件不存在,但该文件所在目录存在返回 true,文件和目录都不存在返回 false,如果文件存在返回 false。
if (-f $request_filename){
	# 判断请求的文件是否存在
}
if (!-f $request_filename){
	# 判断请求的文件是否不存在
}

例如:用户访问的页面不存在,则返回一个友好的提示

server {
    listen 80;
    server_name mayanan.cn;
    charset utf-8;
    default_type text/html;
    location / {
        root html;
        if (!-f $request_filename) {
            return 404 <h1>页面不存在</h1>;
        }
    }
}
  • 判断请求的目录是否存在使用『 -d 』和『 !-d 』
    当使用『 -d 』时,如果请求的目录存在,返回 true,如果目录不存在则返回 false。
    当使用『 !-d 』时,如果请求的目录不存在但该目录的上级目录存在则返回 true,该目录和它上级目录都不存在则返回 false,如果请求目录存在也返回false。

rewrite之break指令

该指令用于中断当前相同作用域中的其他 Nginx 配置。与该指令处于同一作用域的 Nginx 配置中,位于它前面的指令配置生效,位于后面的指令配置无效。并且break还有另外一个功能就是终止当前的匹配并把当前的URI在本location进行重定向访问处理。

语法 默认值 位置
break; server、location、if

例子:

server {
    listen 80;
    charset utf-8;
    default_type text/plain;
    location /testbreak{
        set $username mayanan;
        if ($args){
            set $username maxintong;
            break;
            set $username dongchunxiang;
        }
        add_header username $username;
        return 200 $username;
    }
}

直接访问:http://mayanan.cn/testbreak?1会报错,我们需要在html目录下面新建testbreak目录,在testbreak目录下新建index.html文件才可以。

rewrite之return指令

该指令用于完成对请求的处理,直接向客户端返回响应状态代码。在 return 后的所有 Nginx 配置都是无效的。

语法 默认值 位置
return [text];
return ;
return ; — server、location、if

  • code:返回给客户端的 HTTP 状态代理。可以返回的状态代码为 0 ~ 999 的任意 HTTP 状态代理
  • text:返回给客户端的响应体内容,支持变量的使用和 JSON 字符串
  • URL:跳转给客户端的 URL 地址。

例如:

 server {
    listen 80;
    charset utf-8;
    location / {
        default_type text/plain;
        return 200 "欢迎使用 nginx";
    }
    location /baidu {
        return 302 https://www.baidu.com;
    }
}

此时访问 Nginx,就会在页面看到这句话:欢迎使用 Nginx。
如果访问 /baidu,则跳转到 https://www.baidu.com

posted @ 2022-09-03 11:35  专职  阅读(82)  评论(0编辑  收藏  举报