Nginx Web服务器管理、均衡负载、访问控制与跨域问题

news2024/12/4 15:17:49

Nginx Web 服务器的均衡负载、访问控制与跨域问题


Nginx 的配置

1. 安装Nginx

首先安装Nginx

apt install nginx -y
cacc@purgatory-v:~$ sudo apt install nginx
[sudo] password for cacc:
Reading package lists... Done
Building dependency tree... Done
Reading state information... Done
The following additional packages will be installed:
  iproute2 libatm1 libbpf0 libcap2-bin libdeflate0 libelf1 libgd3 libjbig0 libmaxminddb0 libmnl0 libnginx-mod-http-geoip2
  libnginx-mod-http-image-filter libnginx-mod-http-xslt-filter libnginx-mod-mail libnginx-mod-stream
  libnginx-mod-stream-geoip2 libpam-cap libpam-runtime libtiff5 libwebp7 libx11-6 libx11-data libxau6 libxcb1 libxdmcp6
  libxpm4 libxslt1.1 libxtables12 nginx-common nginx-core
Suggested packages:
  iproute2-doc libgd-tools mmdb-bin fcgiwrap nginx-doc ssl-cert
The following NEW packages will be installed:
  iproute2 libatm1 libbpf0 libcap2-bin libdeflate0 libelf1 libgd3 libjbig0 libmaxminddb0 libmnl0 libnginx-mod-http-geoip2
  libnginx-mod-http-image-filter libnginx-mod-http-xslt-filter libnginx-mod-mail libnginx-mod-stream
  libnginx-mod-stream-geoip2 libpam-cap libpam-runtime libtiff5 libwebp7 libx11-6 libx11-data libxau6 libxcb1 libxdmcp6
  libxpm4 libxslt1.1 libxtables12 nginx nginx-common nginx-core
0 upgraded, 31 newly installed, 0 to remove and 1 not upgraded.
Need to get 3,800 kB of archives.
After this operation, 12.3 MB of additional disk space will be used.

尝试启动Nginx服务

sudo systemctl start nginx
sudo systemctl status nginx
cacc@purgatory-v:~$ sudo systemctl status nginx
○ nginx.service - A high performance web server and a reverse proxy server
     Loaded: loaded (/lib/systemd/system/nginx.service; disabled; vendor preset: enabled)
     Active: inactive (dead)
       Docs: man:nginx(8)
cacc@purgatory-v:~$ sudo systemctl start nginx
Job for nginx.service failed because the control process exited with error code.
See "systemctl status nginx.service" and "journalctl -xeu nginx.service" for details.
cacc@purgatory-v:~$ systemctl status nginx
× nginx.service - A high performance web server and a reverse proxy server
     Loaded: loaded (/lib/systemd/system/nginx.service; disabled; vendor preset: enabled)
     Active: failed (Result: exit-code) since Mon 2024-12-02 21:48:37 CST; 10s ago
       Docs: man:nginx(8)
    Process: 3330 ExecStartPre=/usr/sbin/nginx -t -q -g daemon on; master_process on; (code=exited, status=0/SUCCESS)
    Process: 3331 ExecStart=/usr/sbin/nginx -g daemon on; master_process on; (code=exited, status=1/FAILURE)
        CPU: 51ms

Dec 02 21:48:35 purgatory-v nginx[3331]: nginx: [emerg] bind() to 0.0.0.0:80 failed (98: Unknown error)
Dec 02 21:48:35 purgatory-v nginx[3331]: nginx: [emerg] bind() to [::]:80 failed (98: Unknown error)
Dec 02 21:48:36 purgatory-v nginx[3331]: nginx: [emerg] bind() to 0.0.0.0:80 failed (98: Unknown error)
Dec 02 21:48:36 purgatory-v nginx[3331]: nginx: [emerg] bind() to [::]:80 failed (98: Unknown error)
Dec 02 21:48:36 purgatory-v nginx[3331]: nginx: [emerg] bind() to 0.0.0.0:80 failed (98: Unknown error)
Dec 02 21:48:36 purgatory-v nginx[3331]: nginx: [emerg] bind() to [::]:80 failed (98: Unknown error)
Dec 02 21:48:37 purgatory-v nginx[3331]: nginx: [emerg] still could not bind()
Dec 02 21:48:37 purgatory-v systemd[1]: nginx.service: Control process exited, code=exited, status=1/FAILURE
Dec 02 21:48:37 purgatory-v systemd[1]: nginx.service: Failed with result 'exit-code'.
Dec 02 21:48:37 purgatory-v systemd[1]: Failed to start A high performance web server and a reverse proxy server.

发现无法启动,后来想起来我在这个服务器上的SpringBoot测试项目占用了80端口,关闭后尝试重启发现启动成功。

cacc@purgatory-v:~$ sudo systemctl stop demo
cacc@purgatory-v:~$ sudo systemctl restart nginx
cacc@purgatory-v:~$ systemctl status nginx
● nginx.service - A high performance web server and a reverse proxy server
     Loaded: loaded (/lib/systemd/system/nginx.service; disabled; vendor preset: enabled)
     Active: active (running) since Mon 2024-12-02 21:50:44 CST; 3s ago
       Docs: man:nginx(8)
    Process: 4117 ExecStartPre=/usr/sbin/nginx -t -q -g daemon on; master_process on; (code=exited, status=0/SUCCESS)
    Process: 4118 ExecStart=/usr/sbin/nginx -g daemon on; master_process on; (code=exited, status=0/SUCCESS)
   Main PID: 4119 (nginx)
      Tasks: 5 (limit: 9261)
     Memory: 4.8M
        CPU: 28ms
     CGroup: /system.slice/nginx.service
             ├─4119 "nginx: master process /usr/sbin/nginx -g daemon on; master_process on;"
             ├─4120 "nginx: worker process" "" "" "" "" "" "" "" "" "" "" "" "" >
             ├─4121 "nginx: worker process" "" "" "" "" "" "" "" "" "" "" "" "" >
             ├─4122 "nginx: worker process" "" "" "" "" "" "" "" "" "" "" "" "" >
             └─4123 "nginx: worker process" "" "" "" "" "" "" "" "" "" "" "" "" >

Dec 02 21:50:44 purgatory-v systemd[1]: Starting A high performance web server and a reverse proxy server...
Dec 02 21:50:44 purgatory-v systemd[1]: Started A high performance web server and a reverse proxy server.
cacc@purgatory-v:~$

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传


2. 使用启用其他端口

更改nginx的网站设置,在\etc\nginx\sites-avialable

sudo cp default default-8080
sudo vim default-8080
server {
        listen 8080 default_server;
        listen [::]:8080 default_server;
        root /var/www/html;
        index index.html index.htm index.nginx-debian.html;
        server_name _;
        location / {
                try_files $uri $uri/ =404;
        }
}

设置好后,创建快捷方式到上级目录的启用文件夹的sites-enbales,这里是启用的配置文件,然后测试以下nginx配置文件语法是否正确。

sudo ln -s /etc/nginx/sites-available/default-8080 /etc/nginx/sites-enabled/
sudo nginx -t
cacc@purgatory-v:/etc/nginx $ sudo nginx -t
nginx: the configuration file /etc/nginx/nginx.conf syntax is ok
nginx: configuration file /etc/nginx/nginx.conf test is successful

随后重启Nginx服务,随即访问相应连接,测试成功。

sudo systemctl restart nginx

在这里插入图片描述

在这里插入图片描述


3. 均衡负载

通过Nginx实现均衡负载,首先需要两个作为网站服务器的主机,以及一个代理服务器。代理服务器作为与客户端的窗口,处理客户端的请求。首先客户端发送请求给代理,代理再根据相关算法,将请求接给后端服务器,例如轮询和最小连接数算法,将服务器的压力分散,实现均衡负载

在这里插入图片描述

首先在另一台设备中安装nginx并开启服务

在这里插入图片描述

在这里,192.168.0.115是另一台服务器。将本服务器设置好后无需再设置其他。回到192.168.0.190这个服务器,设置Nginx反向代理,实现均衡负载。

sudo vim sites-available/balance
sudo ln -s /etc/nginx/sites-available/balance /etc/nginx/sites-enabled/

在这里,首先设置后端服务器的列表,这里有两个后端服务器,一个是本身服务器,这里可以填写localhost,另一个则是刚刚建立的192.168.0.115。这里设置了域名分别,也就是通过域名iot.arorms.cn访问机器时(域名解析已经设置成192.168.0.190),就会访问到这个虚拟主机的服务端,并根据这个代理实现均衡负载访问服务器,而IP直接访问则是直接访问。

upstream backend {
    server localhost;
    server 192.168.0.115;
}

server {
    listen 80;
    server_name iot.arorms.cn;

    location / {
        proxy_pass http://backend;
        proxy_set_header Host $host;
        proxy_set_header X-Real-IP $remote_addr;
        proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
        proxy_set_header X-Forwarded-Proto $scheme;
    }
}
sudo nginx -t
sudo systemctl restart nginx
cacc@purgatory-v:nginx/sites-enabled $ systemctl restart nginx
==== AUTHENTICATING FOR org.freedesktop.systemd1.manage-units ====
Authentication is required to restart 'nginx.service'.
Authenticating as: Cacciatore (cacc)
Password:
==== AUTHENTICATION COMPLETE ====

在这里插入图片描述

访问代理设置的域名,即为iot.arorms.cn,访问成功。这里可以直接确定实现了均衡负载,因为在这个设置中,没有写网站的根目录,而代理直接转发给了之前设置的默认配置的网站,说明已经实现了转发。

在这里插入图片描述

还可以查看日志,验证是否成功

sudo tail -f /var/log/nginx/access.log
cacc@purgatory-v:nginx/sites-enabled $ sudo tail -f /var/log/nginx/access.log

192.168.0.158 - - [03/Dec/2024:15:11:59 +0800] "GET / HTTP/1.1" 304 0 "-" "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/131.0.0.0 Safari/537.36"
192.168.0.190 - - [03/Dec/2024:15:11:59 +0800] "GET / HTTP/1.0" 304 0 "-" "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/131.0.0.0 Safari/537.36"
192.168.0.158 - - [03/Dec/2024:15:11:59 +0800] "GET / HTTP/1.1" 304 0 "-" "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/131.0.0.0 Safari/537.36"
192.168.0.190 - - [03/Dec/2024:15:11:59 +0800] "GET / HTTP/1.0" 304 0 "-" "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/131.0.0.0 Safari/537.36"
192.168.0.158 - - [03/Dec/2024:15:11:59 +0800] "GET / HTTP/1.1" 304 0 "-" "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/131.0.0.0 Safari/537.36"
192.168.0.190 - - [03/Dec/2024:15:11:59 +0800] "GET / HTTP/1.0" 304 0 "-" "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/131.0.0.0 Safari/537.36"
192.168.0.158 - - [03/Dec/2024:15:11:59 +0800] "GET / HTTP/1.1" 304 0 "-" "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/131.0.0.0 Safari/537.36"
192.168.0.158 - - [03/Dec/2024:15:11:59 +0800] "GET / HTTP/1.1" 304 0 "-" "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/131.0.0.0 Safari/537.36"
192.168.0.190 - - [03/Dec/2024:15:11:59 +0800] "GET / HTTP/1.0" 304 0 "-" "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/131.0.0.0 Safari/537.36"
192.168.0.158 - - [03/Dec/2024:15:11:59 +0800] "GET / HTTP/1.1" 304 0 "-" "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/131.0.0.0 Safari/537.36"

这里日志显示交替两个IP返回客户端请求,说明这里Nginx使用的是默认的轮询方法,做到均衡负载。

在这里有可能会出现两个错误,需要注意。

  1. 413 Request Header or Cookie too large

    这个可能是因为Nginx默认设置的请求头和Cookie限制,前往/etc/nginx/nginx.conf修改即可,在http块中添加如下两行。

    http {
        client_max_body_size 50M;			   	# 调整请求体大小(默认1M)
        large_client_header_buffers 4 16k;		# 调整请求头缓冲区大小
        ...
    }
    
  2. 500 Internal Server Error

    这可能是因为设置的后端服务器列表中的服务器无法访问导致错误,需要查看是否可以连接。

    curl -I http://192.168.0.190
    curl -I http://192.168.0.115
    

    必须返回以下头部才说明服务器可访问。

    HTTP/1.1 200 OK
    Server: nginx/1.18.0 (Ubuntu)
    Date: Tue, 03 Dec 2024 07:03:58 GMT
    Content-Type: text/html
    Content-Length: 612
    Last-Modified: Tue, 15 Oct 2024 01:14:07 GMT
    Connection: keep-alive
    ETag: "670dc1df-264"
    Accept-Ranges: bytes
    
    HTTP/1.1 200 OK
    Server: nginx/1.22.1
    Date: Tue, 03 Dec 2024 07:03:59 GMT
    Content-Type: text/html
    Content-Length: 615
    Last-Modified: Tue, 03 Dec 2024 01:39:40 GMT
    Connection: keep-alive
    ETag: "674e615c-267"
    Accept-Ranges: bytes
    

    在第四个实验中,为了减少不必要的麻烦,暂时关闭代理。删除sites-enabled下的balance,这里只是删除了它的快捷方式,配置文件仍然在sites-available中,不过因为没有在enabled文件夹中创建快捷方式,所以这个设置没有被启动。

    cacc@purgatory-v:nginx/sites-enabled $ sudo rm balance
    cacc@purgatory-v:nginx/sites-enabled $ sudo systemctl restart nginx
    

基于DNS解析的均衡负载

在这里不难看出,即使已经将所有的业务分散给后端服务器,但是仍然需要通过访问代理服务器来进行访问。这个方案中,仍然需要利用代理服务器来确定连接,而且所有流量都会通过这个代理服务器。还有一种均衡负载的方法,就是通过DNS解析轮询不同服务器的IP,将所有业务均衡负载给这些服务器群,使其能够实现均衡负载。

在这里,已经设置域名解析的DNS记录,abc.arorms.cn设置了三个IP,查看DNS是否生效。下面这段Java代码使用网络地址方法InetAddress.getAllByName(),对域名获取所有解析出来的地址。

package Network;

import java.io.*;
import java.net.*;

/**
 * This program demonstrates the InetAddress class. Supply a host name as command-line
 * argument, or run without command-line arguments to see the address of the local host.
 * @version 1.02 2012-06-05
 * @author Cay Horstmann
 */
public class InetAddressTest
{
   public static void main(String[] args) throws IOException
   {
      if (args.length > 0)
      {
         String host = args[0];
         InetAddress[] addresses = InetAddress.getAllByName(host);
         for (InetAddress a : addresses)
            System.out.println(a);
      }
      else
      {
         InetAddress localHostAddress = InetAddress.getLocalHost();
         System.out.println(localHostAddress);
      }
   }
}

执行后发现成功实现将同一域名访问分散到不同服务器以实现均衡负载。

D:\Environment\jdk-21\bin\java.exe "-javaagent:D:\Development\IntelliJ IDEA 2024.1.4\lib\idea_rt.jar=36143:D:\Development\IntelliJ IDEA 2024.1.4\bin" -Dfile.encoding=UTF-8 -Dsun.stdout.encoding=UTF-8 -Dsun.stderr.encoding=UTF-8 -classpath E:\LearningProjects\Java\out\production\CoreJavaVolume;C:\Users\Holme\.m2\repository\junit\junit\4.13.1\junit-4.13.1.jar;C:\Users\Holme\.m2\repository\org\hamcrest\hamcrest-core\1.3\hamcrest-core-1.3.jar Network.InetAddressTest abc.arorms.cn
abc.arorms.cn/192.168.0.4
abc.arorms.cn/192.168.0.2
abc.arorms.cn/192.168.0.3

Process finished with exit code 0
cacc@purgatory-v:~ $ dig abc.arorms.cn	# 利用dig命令验证

; <<>> DiG 9.18.28-0ubuntu0.22.04.1-Ubuntu <<>> abc.arorms.cn
;; global options: +cmd
;; Got answer:
;; ->>HEADER<<- opcode: QUERY, status: NOERROR, id: 45124
;; flags: qr rd ra; QUERY: 1, ANSWER: 3, AUTHORITY: 6, ADDITIONAL: 12

;; OPT PSEUDOSECTION:
; EDNS: version: 0, flags:; udp: 65494
;; QUESTION SECTION:
;abc.arorms.cn.                 IN      A

;; ANSWER SECTION:
abc.arorms.cn.          600     IN      A       192.168.0.3
abc.arorms.cn.          600     IN      A       192.168.0.4
abc.arorms.cn.          600     IN      A       192.168.0.2

;; AUTHORITY SECTION:
cn.                     44639   IN      NS      c.dns.cn.
cn.                     44639   IN      NS      a.dns.cn.
cn.                     44639   IN      NS      e.dns.cn.
cn.                     44639   IN      NS      d.dns.cn.
cn.                     44639   IN      NS      b.dns.cn.
cn.                     44639   IN      NS      ns.cernet.net.

;; ADDITIONAL SECTION:
a.dns.cn.               44639   IN      A       203.119.25.1
b.dns.cn.               44639   IN      A       203.119.26.1
c.dns.cn.               44639   IN      A       203.119.27.1
d.dns.cn.               44639   IN      A       203.119.28.1
e.dns.cn.               44639   IN      A       203.119.29.1
ns.cernet.net.          44639   IN      A       202.112.0.44
a.dns.cn.               44639   IN      AAAA    2001:dc7::1
b.dns.cn.               44639   IN      AAAA    2001:dc7:1::1
c.dns.cn.               44639   IN      AAAA    2001:dc7:2::1
d.dns.cn.               44639   IN      AAAA    2001:dc7:1000::1
e.dns.cn.               44639   IN      AAAA    2001:dc7:3::1

;; Query time: 96 msec
;; SERVER: 127.0.0.53#53(127.0.0.53) (UDP)
;; WHEN: Tue Dec 03 20:28:46 CST 2024
;; MSG SIZE  rcvd: 437

4. 访问控制

在服务器开启Nginx的访问控制,有很多方案。

  • 基于IP地址的访问控制,用于限制从其他网络连接,比如限制外部IP,放行192.168.0.0/24
  • 基于User-Agent的访问控制,是请求头的一个属性,标识了用户身份,比如运行正常用户通过浏览器访问网站,但是不允许爬虫直接访问爬取网站资源。
  • 基于Referer的访问控制,这个用来允许或者拒绝某些来源的请求。最简单的比如只允许站内跳转到本页面。
  • 基于请求方法的控制,例如只允许GETPOST方法访问。
  • 基于HTTP身份验证的访问控制,为网站设置用户名和密码
  • 基于时间、地理位置的访问控制。
  • . . . . . .
HTTP身份验证

首先安装apache2-utils工具,用于创建和管理用户密码文件。

sudo apt-get update
sudo apt-get install apache2-utils

然后创建密码文件,利用htpasswd命令创建一个包含用户密码的文件。新建一个密码文件,并设置用户名为cacc

sudo htpasswd -c /etc/nginx/.htpasswd cacc
cacc@purgatory-v:nginx/sites-enabled $ sudo htpasswd -c /etc/nginx/.htpasswd cacc
New password:
Re-type new password:
Adding password for user cacc

可以查看密码,可以看出已经被单向算法加密

cacc@purgatory-v:nginx/sites-enabled $ cat /etc/nginx/.htpasswd
cacc:$apr1$oyMbSbhj$arjSai4uK4lEwUxibxSZ/.

创建完密码文件后,返回默认配置文件/etc/nginx/sites-enabled/default,进行更改。

sudo vim /etc/nginx/sites-enabled/default
server {
        listen 80 default_server;
        listen [::]:80 default_server;

        root /var/www/html;
        index index.html index.nginx-debian.html;
        server_name iot.arorms.cn;

        location / {
                auth_basic "Restricted Access";					# 提示信息
                auth_basic_user_file /etc/nginx/.htpasswd;		# 密码文件
                try_files $uri $uri/ =404;
        }
}

验证Nginx设置语法正确性然后重新启动Nginx服务,重新访问网站,就可以发现需要输入用户名密码才可以访问网站资源。

在这里插入图片描述

输入密码后访问成功

在这里插入图片描述

IP访问控制

注释掉刚刚身份验证控制的设置,并更换为如下设置。

server {
        listen 80 default_server;
        listen [::]:80 default_server;

        root /var/www/html;
        index index.html index.nginx-debian.html;
        server_name iot.arorms.cn;

        location / {
                allow 192.168.0.158;	# 允许192.168.0.158访问
                deny all;				# 拒绝其他所有连接
                #auth_basic "Restricted Access";
                #auth_basic_user_file /etc/nginx/.htpasswd;
                try_files $uri $uri/ =404;
        }
}

利用两台不同的机器测试,由于服务器没有安装图形化界面,直接通过curl调用接口的命令访问服务器。

192.168.0.158 Windows的访问结果,由于本IP被设置在白名单上,因此可以访问。

Invoke-WebRequest iot.arorms.cn -UseBasicParsing | Select-Object -ExpandProperty Content

返回网站页面

<!DOCTYPE html>
<html>
<head>
<title>Welcome to nginx!</title>
<style>
    body {
        width: 35em;
        margin: 0 auto;
        font-family: Tahoma, Verdana, Arial, sans-serif;
    }
</style>
</head>
<body>
<h1>Welcome to nginx!</h1>
<p>If you see this page, the nginx web server is successfully installed and
working. Further configuration is required.</p>

<p>For online documentation and support please refer to
<a href="http://nginx.org/">nginx.org</a>.<br/>
Commercial support is available at
<a href="http://nginx.com/">nginx.com</a>.</p>

<p><em>Thank you for using nginx.</em></p>
</body>
</html>

192.168.0.190 Ubuntu访问结果,不在白名单上,无法访问。

curl iot.arorms.cn

返回结果可以看到错误403 Forbidden无法访问。

<html>
<head><title>403 Forbidden</title></head>
<body>
<center><h1>403 Forbidden</h1></center>
<hr><center>nginx/1.18.0 (Ubuntu)</center>
</body>
</html>

跨域问题的解决

在B/S 的应用中,什么是跨域问题?一般跨域问题的解决思路有哪些?在Nginx 中能否解决跨域问题?如何能,如何配置实现?并实验验证它。

URL结构

在跨域问题中,首先需要明确URL结构,一个URL通常如下,例如一个显示新闻的页面可能是这样的。

http://iot.arorms.cn:8080/news?id=21#title
  • http 协议
  • iot.arorms.cn 服务器域名
  • 8080 端口
  • news 资源路径
  • id=21 参数,这里是id为21
  • #title 锚点,用于定位网页位置

当访问web内容时,需要通过URL进行定位,通常会返回一个资源对象,当不同对象的协议,主机(域名),端口相同时,则称它们有共同的源。

跨域问题

如果不同源,会限制以下内容:

  • DOM 操作:不同源无法访问彼此的 DOM。

  • AJAX 请求:浏览器会阻止跨源的异步请求。

  • Cookie、LocalStorage 和 SessionStorage:限制不同源访问存储数据。

浏览器的同源策略是一个重要的安全策略,它用于限制一个源的文档或者它加载的脚本如何能与另一个源的资源进行交互。它能够帮助阻隔恶意文档,避免跨网站脚本攻击,例如攻击者通过注入恶意脚本,窃取用户的Cookie或者敏感数据,限制注入的脚本与目标节点交互,可以减少攻击媒介。其次攻击者伪装成受信用户向服务器发送而已请求,同源策略能够限制第三方站点直接操作用户的敏感数据。

在我之前的项目中,用户需要注册登录网站。在此时会生成一份PHPSESSION,作为身份验证。而通过XSS注入,可以通过脚本将用户的PHPSESSION发送到攻击者的服务器中记录。而攻击者的服务器肯定与被攻击者的网站不同源,因此限制同源访问可以解决一些攻击手段。不过同源策略无法低于明显的跳转式SESSION劫持。

在前后端不分离时,通常不存在跨域问题,因为资源基本上都在同一个服务器。而前后端分离后,由于前端需要调用后端API,由于前后端分离,服务器不同,会导致跨域问题。

Nginx解决方案

设置反向代理

在Nginx的设置文件中如下

server {
    listen 80;
    server_name iot.arorms.cn;

    location /api/ {
        proxy_pass http://api.arorms.cn; # 目标跨域服务
        proxy_set_header Host $host;
        proxy_set_header X-Real-IP $remote_addr;
        proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
    }
}

客户端访问http://iot.arorms.cn/api时,通过反向代理调用http://api.arorms.cn服务器的api,而客户端仍然认为时iot.arorms.cn返回的资源。

验证

首先先看一下以下典型场景是否会引发跨域问题。

  1. 重定向,不会引发跨域问题

    • html标签href跳转,如下连接将用户页面重定向到主页,可以访问

      <href a="http://iot.arorms.cn">访问主页</href>
      
      • Spring方法redirect重定向网页,不会出现跨域问题
  2. 跨域获取资源

    • JavaScript的fetchXMLHttpRequestAJAX方法获取资源

      在这里跨域获取资源是不被允许的

在前面的实验中,出现了SpringBoot占用80端口导致Nginx无法启动的失误。在服务器上我利用Java SpringBoot写了一些后端API可以调用,可以用SpringBoot项目中的API做验证。

首先,后端的调用接口如下,此处是获取镇江(city=321100)天气的接口。

http://api.arorms.cn/weather/getByCity?city=321100

利用SpringBoot框架所创建的天气查询功能中,所对应的Controller类代码如下

package cn.arorms.demo.controller;

import cn.arorms.demo.entity.WeatherInfo;
import cn.arorms.demo.service.WeatherService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.RestController;

import java.util.List;

/**
 * WeatherController
 * @version 1.0 2024-11-06
 * @author Cacc
 */
@RestController
@RequestMapping("/weather")
public class WeatherController {
    @Autowired
    WeatherService weatherService;

    @GetMapping("/getByCity")
    public List<WeatherInfo> getByCity(@RequestParam("city") String city) {
        return weatherService.getWeatherForcastsByCity(city);
    }
}

首先,通过直接调用端口API,可以发现能够直接获取数据

curl http://api.arorms.cn/weather/getByCity\?city\=321100

返回数据,返回了天气信息对象,这里已经将json数据美化。

[
    {
        "city": "镇江市",
        "date": "2024-12-03",
        "weekNumber": "2",
        "dayTemp": "15",
        "nightTemp": "6",
        "dayWeather": "多云",
        "nightWeather": "阴"
    },
    
    // 还有还有后面三天预测省略
]

在另一台服务器上,启动前端页面,前端代码如下

<!DOCTYPE html>
<html lang="cn">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>天气预报</title>
    <link href="/css/bootstrap.min.css" rel="stylesheet">
    <!-- 此处省略css代码 -->
</head>
<body>
<div class="container">
    <div class="row justify-content-center">
        <div class="col-md-5">
            <div class="weather-container">
                <h2>镇江天气预报</h2>
                <div id="forecastContainer"></div>
                <button class="btn btn-primary mt-4" onclick="fetchWeather()">获取天气</button>
                <div id="errorMessage" class="error-message" style="display: none;"></div>
            </div>
        </div>
    </div>
</div>

<script src="/js/bootstrap.bundle.min.js"></script>
<script>
    function fetchWeather() {
        const cityId = '321100';
        const apiUrl = `http://api.arorms.cn/weather/getByCity?city=${cityId}`;

        document.getElementById('errorMessage').style.display = 'none';

        fetch(apiUrl)
            .then(response => response.json())
            .then(data => {
                if (data && Array.isArray(data)) {
                    const forecastContainer = document.getElementById('forecastContainer');
                    forecastContainer.innerHTML = '';

                    data.forEach(weather => {
                        const forecastDay = document.createElement('div');
                        forecastDay.classList.add('forecast-day');

                        const date = weather.date;
                        const dayTemp = `${weather.dayTemp}°C`;
                        const nightTemp = `${weather.nightTemp}°C`;
                        const dayWeather = `白天: ${weather.dayWeather}`;
                        const nightWeather = `夜间: ${weather.nightWeather}`;

                        forecastDay.innerHTML = `
                            <h4>${formatDate(weather.date)}</h4>
                            <div>${dayWeather} | ${dayTemp}</div>
                            <div>${nightWeather} | ${nightTemp}</div>
                        `;

                        forecastContainer.appendChild(forecastDay);
                    });
                } else {
                    showError('无法获取天气数据,请稍后再试');
                }
            })
            .catch(error => {
                console.error('获取天气数据时发生错误:', error);
                showError('发生错误,请检查网络连接');
            });
    }

    function formatDate(dateString) {
        const date = new Date(dateString);
        const month = date.getMonth() + 1;
        const day = date.getDate();

        return `${month}${day}`;
    }


    function showError(message) {
        const errorMessageDiv = document.getElementById('errorMessage');
        errorMessageDiv.style.display = 'block';
        errorMessageDiv.innerText = message;
    }

    window.onload = function() {
        fetchWeather();
    };
</script>
</body>
</html>

这里利用JavaScript的fetch,将调用api获取的对象显示到前端。而此处发现无法直接访问,因为这里触发了同源策略中主机不同的问题,导致无法执行脚本返回数据。也就是直接调用后端API会导致无法获取资源。

const apiUrl = `http://api.arorms.cn/weather/getByCity?city=${cityId}`;

直接访问http://iot.arorms.cn,发现无法调用接口。

在这里插入图片描述

这时,在服务器iot.arorms.cn上设置Nginx反向代理,解决跨域问题。

server {
    listen 80;
    server_name iot.arorms.cn;

    location / {
        root /var/www/demo;
        index index.html;
    }

    location /api/ {
        proxy_pass http://api.arorms.cn/weather/;
        proxy_set_header Host api.arorms.cn;
        proxy_set_header X-Real-IP $remote_addr;
        proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
        proxy_set_header X-Forwarded-Proto $scheme;
    }
}

在这里插入图片描述

通过这样的设置,可以看出,由于数据是根据服务器的反向代理获取的,因此解决了不同源的问题,使得数据可以返回到前端显示给用户。

完成设置后测试Nginx语法并重启Nginx。将weather.html放入demo文件夹根目录后,访问iot.arorms.cn直接进入天气查询页面。在天气查询的代码中,更改apiUrl为代理的url。

const apiUrl = `http://iot.arorms.cn/api/getByCity?city=${cityId}`;

重新访问http://iot.arorms.cn并查询天气,查询成功,也验证了Nginx反向代理方法可以解决跨域问题。

在这里插入图片描述

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/2253107.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

Bert+CRF的NER实战

CRF&#xff08;条件随机场-Conditional Random Field&#xff09; 原始本文&#xff1a;我在北京吃炸酱面 标注示例&#xff08;采用BIO标注方式&#xff09;&#xff1a; 我O在O北B-PLA京I-PLA吃O炸B-FOOD酱I-FOOD面I-FOOD CRF&#xff1a; 目的&#xff1a;提出一些不可能…

C++语法·识

人生建议&#xff1a;请手机反省一下&#xff0c;为什么总拉着我熬夜。 目录 STL简介 string类容器一 auto&#xff08;自动声明类型&#xff09; 简介&#xff1a; 特点 范围for&#xff08;语法糖&#xff09; 简介 特点 string string类的常见接口 1.构造 2.容…

蓝桥杯准备训练(lesson1,c++方向)

前言 报名参加了蓝桥杯&#xff08;c&#xff09;方向的宝子们&#xff0c;今天我将与大家一起努力参赛&#xff0c;后序会与大家分享我的学习情况&#xff0c;我将从最基础的内容开始学习&#xff0c;带大家打好基础&#xff0c;在每节课后都会有练习题&#xff0c;刚开始的练…

【开源】A059-基于SpringBoot的社区养老服务系统的设计与实现

&#x1f64a;作者简介&#xff1a;在校研究生&#xff0c;拥有计算机专业的研究生开发团队&#xff0c;分享技术代码帮助学生学习&#xff0c;独立完成自己的网站项目。 代码可以查看项目链接获取⬇️&#xff0c;记得注明来意哦~&#x1f339; 赠送计算机毕业设计600个选题ex…

winform跨线程更新界面

前言&#xff1a; 大家好&#xff0c;我是上位机马工&#xff0c;硕士毕业4年年入40万&#xff0c;目前在一家自动化公司担任软件经理&#xff0c;从事C#上位机软件开发8年以上&#xff01;我们在开发C#程序的时候&#xff0c;有时候需要在非Ui主线程更新界面&#xff0c;为了…

无界(wujie)微前端项目搭建,nginx线上部署,pnpm一键安装依赖、启动应用,git代码仓库存放方式

这里写自定义目录标题 1. 创建项目项目目录布局选择主应用子应用 2. pnpm包管理&#xff0c;一键安装、启动、打包pnpm一键安装依赖npm-run-all 一键启动、打包 3. nginx线上部署主应用中子应用中nginx文件目录及配置 git代码存放方式 1. 创建项目 主应用&#xff1a; vue3vit…

10.容器-list列表

定义一个list使用[] 定义一个空列表 [] 或者 list() 列表中每个元素之间用逗号隔开 a_list [aa, bb, cc] print(a_list) # <class list> print(type(a_list)) list列表可以存储不同类型的元素 a_list [aa, bb, cc] print(a_list) # <class list> print(type…

BiGRU:双向门控循环单元在序列处理中的深度探索

一、引言 在当今的人工智能领域&#xff0c;序列数据的处理是一个极为重要的任务&#xff0c;涵盖了自然语言处理、语音识别、时间序列分析等多个关键领域。循环神经网络&#xff08;RNN&#xff09;及其衍生结构在处理序列数据方面发挥了重要作用。然而&#xff0c;传统的 RN…

PDF与PDF/A的区别及如何使用Python实现它们之间的相互转换

目录 概述 PDF/A 是什么&#xff1f;与 PDF 有何不同&#xff1f; 用于实现 PDF 与 PDF/A 相互转换的 Python 库 Python 实现 PDF 转 PDF/A 将 PDF 转换为 PDF/A-1a 将 PDF 转换为 PDF/A-1b 将 PDF 转换为 PDF/A-2a 将 PDF 转换为 PDF/A-2b 将 PDF 转换为 PDF/A-3a 将…

计费结算系统的架构设计思路

背景 近期负责关于集团的计费结算相关的系统&#xff0c;相对于2C系统的大流量&#xff0c;高并发的场景&#xff0c;计费和结算的信息对稳定性要求更高。对时效性要求并没有过于严苛的要求。那么接下来就和大家分享一下计费结算系统的架构设计。 模块划分 我们暂且将平台细分…

人工智障(5)

今天kimi把我气疯了&#xff0c;你们看原对话&#xff1a; 月之暗面最近在搞什么&#xff0c;不仅算力慢&#xff0c;而且回答离谱的要死&#xff0c;难道换老板了&#xff1f;

Python爬虫——城市数据分析与市场潜能计算(Pandas库)

使用Python进行城市市场潜能分析 简介 本教程将指导您如何使用Python和Pandas库来处理城市数据&#xff0c;包括GDP、面积和城市间距离。我们将计算每个城市的市场潜能&#xff0c;这有助于了解各城市的经济影响力。 步骤 1: 准备环境 确保您的环境中安装了Python和以下库&…

Python毕业设计选题:基于Flask的医疗预约与诊断系统

开发语言&#xff1a;Python框架&#xff1a;flaskPython版本&#xff1a;python3.7.7数据库&#xff1a;mysql 5.7数据库工具&#xff1a;Navicat11开发软件&#xff1a;PyCharm 系统展示 系统首页 疾病信息 就诊信息 个人中心 管理员登录界面 管理员功能界面 用户界面 医生…

Android 图形系统之二:ViewRootImpl

ViewRootImpl简介 ViewRootImpl 是 Android UI 系统的核心类之一&#xff0c;负责将 View 层级树与窗口管理器 WindowManager 联系起来。它是Android 应用视图的根节点&#xff0c;与 WindowManager 结合&#xff0c;实现视图的绘制、事件分发、窗口更新等功能。虽然 ViewRoot…

python通过ODBC连接神通数据库

1、安装神通数据库 2、安装python 3、安装pyodbc pip3 install pyodbc-5.2.0-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl 注&#xff1a;pyodbc要和python版本相对应 4、安装unixodbc 5、配置神通数据库ODBC数据源 6、示例代码如下 #!/usr/bin/python…

基于单片机的智能药箱设计

本设计主要由红外检测传感器、显示、独立按键、舵机、语音以及短信等模块组成。红外传感器模块主要对药仓中的药物数据进行采集&#xff0c;采集完毕由主控制器进行数据加工&#xff0c;之后可传送至显示模块上进行显示&#xff0c;在显示模块也可对显示时间、吃药倒计时、吃药…

【掩体计划——DFS+缩点】

题目 代码 #include <bits/stdc.h> using namespace std; const int N 1e5 10; vector<vector<int>> g; bool st[N]; int ans 1e9; bool dfs(int f, int u, int dis) {bool is 1;for (auto j : g[u]){if (j f)continue;is & dfs(u, j, dis (g[u].…

无人机点云处理算法技术解析!

一、核心技术 数据预处理&#xff1a; 数据预处理是点云处理的第一步&#xff0c;主要包括滤波、去噪、数据压缩等。滤波技术可以去除点云数据中的噪声和孤立点&#xff0c;提高数据质量。常用的滤波方法包括双边滤波、高斯滤波等。 数据压缩则用于减少数据量&#xff0c;提…

Android13 允许桌面自动旋转

一&#xff09;需求-场景 Android13 实现允许桌面自动旋转 Android13 版本开始后&#xff0c;支持屏幕自动旋转&#xff0c;优化体验和兼容性&#xff0c;适配不同屏幕 主界面可自动旋转 二&#xff09;参考资料 android framework13-launcher3【06手机旋转问题】 Launcher默…

vue+uniapp+echarts的使用(H5环境下echarts)

1.安装 npm install echarts4.9.0 --save // 带版本号 2.main.js中全局引用 // import echarts from echarts // 如果是5.0以上版本用这个 import * as echarts from echarts Vue.prototype.$echartsecharts 3.使用 <template><view id"box" style"w…