懒猫微服开发篇(六):用 Openresty 做反向代理来解决跨域问题

忘机山人

发布于334天前
博客图片修整中,看不了可以先搜索公众号“忘机山人”看。
对于 Web 开发而言, 经常会遇到跨域问题。我们先来看一下什么是跨域问题:

**跨域问题(Cross-Origin)**本质上是浏览器的**同源策略(Same-Origin Policy, SOP)**在发挥作用:

> **同源**指“协议 + 域名(或 IP)+ 端口”三要素完全一致。只要三者有任何一个不同,就被视为**跨域**。

#### 为什么浏览器要限制跨域?

- **安全**:阻止一个站点随意读取或修改另一个站点的敏感资源(如 Cookie、LocalStorage、DOM),避免 XSS、CSRF 等攻击链被无限放大。
- **隔离**:让不同网站在沙盒里各自运行,互不干扰。

> 同源策略只在**浏览器环境**生效;后端服务之间(如服务器 A 请求服务器 B)并没有 SOP 的限制。

 

| 场景                                                         | 描述           | 是否受限 |
| ------------------------------------------------------------ | -------------- | -------- |
| `fetch('https://api.foo.com')` 从 `https://www.bar.com` 发出 | 协议、域名不同 | **受限** |
| `http://example.com:3000` 调用 `http://example.com:4000`     | 端口不同       | **受限** |

> ⚠️ **用 Nginx/OpenResty 并不会“自动”解决 CORS**。
>
> - 你可以把前端请求代理到后端 API,使浏览器认为请求仍在同一域名下,达到“变同源”的效果。
> - 或者直接在后端/代理层加 CORS 响应头,两种方式都可以。

懒猫微服的上使用的是 OpenResty,这是一个功能齐全的 Web 应用服务器,它集成了标准的 nginx core、大量第三方 nginx 模块以及它们的大部分外部依赖项。所以和 Nginx 的配置文件是通用的。

以我之前比赛做的项目为例,这个是 Nginx 作为网关,监听 80 端口,然后反向代理到 Next.js 和 Flask。

```yml
services:
  nginx:
    build:
      context: .
      dockerfile: Dockerfile
    ports:
      - "80:80"
    depends_on:
      - next-app
      - backend
    restart: unless-stopped

  next-app:
    image: smart-shopping-app
    container_name: next-frontend
    expose:
      - "3000"
    environment:
      - NODE_ENV=production
      - NEXT_TELEMETRY_DISABLED=1
    restart: unless-stopped

  backend:
    image: shoppingassistant-backend
    container_name: backend-app
    expose:
      - "5005"
    restart: unless-stopped
```

而 Nginx 的配置文件如写,做七成的转发,把根路径转发到前端,/api 转发到后端。所以前端的 axios 请求等于访问的/api 这个端点,所以可以规避跨域的问题。

```conf
server {
    listen 80;
    server_name localhost;

    location / {
        proxy_pass http://next-app:3000;
        proxy_set_header Host $host;
        proxy_set_header X-Real-IP $remote_addr;
        proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
    }

    location /api {
        proxy_pass http://backend:5005;
        proxy_set_header Host $host;
        proxy_set_header X-Real-IP $remote_addr;
        proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
    }
}
```

其实对于懒猫微服的 OpenResty 的也是一样的,好处是不用自己再找 base image 了,直接把配置文件写进去就能用了。

```yml
lzc-sdk-version: "0.1"
name: APP Proxy Test
package: cloud.lazycat.app.app-proxy-test
version: 0.0.1
application:
  routes:
    - /=http://app-proxy.cloud.lazycat.app.app-proxy-test.lzcapp:80
  subdomain: app-proxy-test #
services:
  app-proxy:
    image: registry.lazycat.cloud/app-proxy:v0.1.0
    setup_script: |
      cat  /etc/nginx/conf.d/default.conf
      server {
          listen 80;
          server_name _;

          # 静态页面
          location / {
              root   /usr/local/openresty/nginx/html;
              index  index.html index.htm;
          }

          # API 反向代理,保留 /api 前缀
          location /api/ {
              proxy_pass http://flask:5000/;
              proxy_set_header Host $host;
              proxy_set_header X-Real-IP $remote_addr;
              proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
          }
      }
      EOF
  flask:
    image: registry.lazycat.cloud/u04123229/cloudsmithy/flask-demo:c14689303facd82c
```

flask 的 image 是我之前做的一个镜像仓库`docker run -p 5005:5000 cloudsmithy/flask-demo:latest`

然后通过`lzc-cli appstore copy-image cloudsmithy/flask-demo` 把镜像换成懒猫的镜像,`registry.lazycat.cloud/u04123229/cloudsmithy/flask-demo:c14689303facd82c`

通过`setup_script`传入和 Nginx 类似的配置文件,原理是替换 docker image 本身的 entrypoint/command 参数。

```bash
# 打包成 LPK
lzc-cli project build -o release.lpk

# 在线安装 LPK
lzc-cli app install release.lpk
```

我们可以看到这个是 OpenResty 的主页,然后访问`https://app-proxy-test.micro.heiyu.space/api/` 也能返回 Flask 容器“Hello from multi-arch Flask Docker in production mode!”。
![image-20250704120859972](https://raw.githubusercontent.com/cloudsmithy/picgo-imh/master/image-20250704120859972.png)

如果你想把根路由直接代理到容器,也可以使用这个办法。这个一般是用来做反向代理来访问内网的服务,即使是 http 也没有关系。这个环境变量应该是懒猫魔改的快捷方式。不要和配置文件混用。

```yml
lzc-sdk-version: "0.1"
name: APP Proxy Test
package: cloud.lazycat.app.app-proxy-test
version: 0.0.1
application:
  routes:
    - /=http://app-proxy.cloud.lazycat.app.app-proxy-test.lzcapp:80
  subdomain: app-proxy-test #
services:
  app-proxy:
    image: registry.lazycat.cloud/app-proxy:v0.1.0
    environment:
      - UPSTREAM="http://flask:5000"
  flask:
    image: registry.lazycat.cloud/u04123229/cloudsmithy/flask-demo:c14689303facd82c
```

有时候还会加上`BASIC_AUTH_HEADER`这个字段来让 nginx/Openresty 自动填写密码,除了你的容器以外,代理外边服务也行。

其实用`echo -n "user:password" | base64`,的数据来填充`BASIC_AUTH_HEADER`"Basic  "

```yml
lzc-sdk-version: "0.1"
name: APP Proxy Test
package: cloud.lazycat.app.app-proxy-test
version: 0.0.1
application:
  routes:
    - /=http://app-proxy.cloud.lazycat.app.app-proxy-test.lzcapp:80
  subdomain: app-proxy-test #
services:
  app-proxy:
    image: registry.lazycat.cloud/app-proxy:v0.1.0
    environment:
      - UPSTREAM="https://xxx:9200/"
      - BASIC_AUTH_HEADER="Basic YWRt46YzssdsfFlOk="
  flask:
    image: registry.lazycat.cloud/u04123229/cloudsmithy/flask-demo:c14689303facd82c
```

另外也支持多域名解析,这个在传统的线下机房比较常见,而云上基本上还是 7 层基于路由转发,比如第一种,我也更加熟悉第一种。

这个其实就是加了一个 secondary_domains 的字段,然后把后端单独暴露出来了。这样就子域名就可以访问后端。

```yml
lzc-sdk-version: "0.1"
name: APP Proxy Test
package: cloud.lazycat.app.app-proxy-test
version: 0.0.1
application:
  routes:
    - /=http://app-proxy.cloud.lazycat.app.app-proxy-test.lzcapp:80
  subdomain: app-proxy-test # 应用列表里默认打开的域名
  secondary_domains:
    - flask
services:
  app-proxy:
    image: registry.lazycat.cloud/app-proxy:v0.1.0
    setup_script: |
      cat  /etc/nginx/conf.d/default.conf
      server {
         server_name  app-proxy-test.*;
         location / {
            root   /usr/local/openresty/nginx/html;
            index  index.html index.htm;
         }
      }

      server {
         server_name  flask.*;

         location / {
            proxy_pass http://flask:5000;
         }
      }
      EOF
  flask:
    image: registry.lazycat.cloud/u04123229/cloudsmithy/flask-demo:c14689303facd82c
```
    
    
![c65b5d34d8a94561d23a8c6ddf79dfcc.jpg](https://dl.playground.lazycat.cloud/guidelines/459/9d64a5fd-8d84-4834-849b-21d5372dc2a5.jpg "c65b5d34d8a94561d23a8c6ddf79dfcc.jpg")

评论

0

暂无评论

说点什么呢~
收藏
0
0
0