linux服务器部署Logto后端与对应前端的实现

前言

本文是在对应域名上使用linux服务器部署Logto后端与使用其预制前端的实现文章,本文将会详细解释在linux服务器上部署Logto的流程与注意事项。

本文将会使用被提供的域名与被提供的无管理员权限的linux服务器演示部署流程,同时本文中所有 密钥,和私密信息将会被隐藏,读者复现可以使用自己的密钥与信息复现流程。本文使用域名 www.testweb.com(https) 与 auth.testweb.com(https) 作为演示域名,该域名初始没有业务页面,因此需要自行开发主页,对接 Logto 身份回调。

1. 注意事项与流程简要

由于主要部署的是无管理员权限的 linux 服务,所以部署采用 docker 容器运行在后端服务器上,同时需要注意的是,部署的目标软件是 Logto,所以大部分的Docker容器中运行的软件功能为:辅助Logto运行。

实现流程中使用的docker镜像内的软件为: 1. logto (登录系统后端) 2. postgres (Logto需要的数据库) 3. caddy (请求转发) 4. nginx (Nginx TLS 预嗅探四层分流) 5. web(自制express后端请求)

1. 注意事项

Logto 默认监听两个端口: 3001面向用户、OIDC登录页、账号中心。3002是管理控制台。建议不要直接把 3002 暴露公网,本文通过 SSH 本地端口转发访问控制台。

由于Logto本身为后端程序,它的登录预制网页需要由前端网页调用Logto提供的对应网页框架的函数包,同时还需要提供Logto登录完成后的回调处理程序,对应的详细可以参考:

关于 Logto 登录系统的体验以及建议-CSDN博客

因为Logto预制登录页面需要前端触发,所以需要在域名上提供前端网页访问和Logto:3001接口访问。由于使用URL请求分割 /api/* 等前缀来实现请求分离太过麻烦,所以这里演示使用了子域名(auth)作为Logto:3001请求的入口,以及主域名(www)作为用户访问前端网页的入口

2. 部署流程简要

本演示的部署流程可以简要为以下流程图

1.www.testweb.com 与auth.testweb.com 长期开放443转发到调试linux:3000机作为请求入口,短期开放 80 端口到调试linux:3000方便接收ACME证书(ACME HTTP-01 挑战依赖 80 端口,证书签发成功后也建议保留 80,用于自动续期。)

  1. ssh建立调试linux服务器到电脑之间的端口转发,本演示使用 SSH 本地端口转发,将服务器宿主机的 8080 映射到本机,用来访问 Logto 控制台;该端口不向公网开放。

  2. Nginx 容器绑定宿主机3000,作为整个系统流量入口。Nginx 在 stream 层使用ssl_preread:如果是 TLS 流量,转发到 Caddy 的 443;普通 HTTP 流量转发到 Caddy 的 80。

  3. Caddy 收取 80 端口,自动请求和续费ACME证书,对 443 端口区分 auth 与 www, 分别转发至Logto监听的 3001 端口与使用node自制的web容器暴露的3003端口

  4. www请求由web返回对应前端页面,auth请求由logto处理,经由Caddy中转返回网页,完整流程

在 auth 域名完整链路调通前,仅靠 SSH 转发可以进入控制台,但登录、OIDC、账号中心等 auth 域名下的功能无法正常工作,可以作为排查链路的判断依据。

Let's Encrypt 存在速率限制,频繁重启、反复触发ACME容易被限流,冷却约一小时;调试优先走明文 HTTP,确认路由无误后再开启自动 HTTPS。

2. 具体部署流程

以下演示部署的docker-compose.yml为分片服务,文章最后会附上完整docker-compose.yml。

本部署中所有的软件在docker的同一个 logto-net 网络中,方便互相访问

1. 部署 logto 与对应的 postgres

首先使用docker容器部署logto与postgres,本演示的部署使用规则: logto依赖postgres,同时postgres使用持久化存储。docker容器监听宿主机 8080 并直接转发至容器内的3002端口(logto控制台端口),方便本机可视化调试控制台

需要注意的是,第一次启动 postgres 需要附加命令,启动完成后删除

复制代码
entrypoint: ["sh", "-c", "npm run cli db seed && npm start"]

如果希望容器每次启动安全初始化,把 seed 改为

复制代码
 npm run cli db seed --swe && npm start 

--swe (seed-when-empty)只会在数据库为空时写入种子数据,已经存在表时直接跳过,避免唯一主键冲突报错。

复制代码
postgres:
    image: postgres:16-alpine
    environment:
      POSTGRES_USER: logto
      POSTGRES_PASSWORD: logto
      POSTGRES_DB: logto
    volumes:
      - postgres_data:/var/lib/postgresql/data
    healthcheck:
      test: ["CMD-SHELL", "pg_isready -U logto -d logto"]
      interval: 5s
      timeout: 5s
      retries: 5
    networks:
      - logto-net
  logto:
    image: ghcr.io/logto-io/logto:latest
    depends_on:
      postgres:
        condition: service_healthy
    entrypoint: ["sh", "-c", "npm start"]
    ports:
      - "8080:3002"
    environment:
      - HOST=0.0.0.0
      - TRUST_PROXY_HEADER=1
      - DB_URL=postgres://logto:logto@postgres:5432/logto
      - ENDPOINT=https://auth.testweb.com
      - ADMIN_ENDPOINT=http://127.0.0.1:8080
    networks:
      - logto-net

完成第一步docker-compose.yml后,使用ssh连接控制台页面(用你自己的密钥)

复制代码
ssh -p 00000 -i ./key -L 8080:127.0.0.1:8080 user@127.0.0.2

这里如果是第一步转发成功了,页面返回应该是Logto的控制台注册页面,进去后是不断报错的控制台页面,这里我放一张能用的控制台大致参考一下页面

2. 部署 Caddy

完成上述步骤后,部署Caddy用于转发 auth 与 www 的域名。由于有Nginx在前面分流 80 与 443 的功能在,Caddy不需要docker映射端口,但是需要对Caddy的数据进行持久化存储,这样可以在Caddy已经成功申请证书后,防止docker容器重启后Caddy反复申请证书。对应的docker-compose.yml的分片如下

复制代码
caddy:
    image: caddy:latest
    volumes:
      - ./Caddyfile:/etc/caddy/Caddyfile
      - caddy_data:/data
      - caddy_config:/config
    depends_on:
      - logto
    networks:
      - logto-net

同时caddy也需要自己的Caddyfile,当前演示只用处理 auth 与 www 即可,演示Caddyfile如下

复制代码
auth.testweb.com {
        reverse_proxy logto:3001 {
                header_up Host {host}
                header_up X-Forwarded-Server {server_name}
                header_up X-Forwarded-Proto {scheme}
                header_up X-Forwarded-For {remote}
        }
}
www.testweb.com {
        reverse_proxy web:3003
}

上文Caddyfile中Caddy会自动处理https请求,但是从刚刚开始的调试来讲,应该先测试http请求能否处理,然后再测试https,这样做可以缩减可能发生的报错的范围,尽量让调试报错可处理,所以原始测试的Caddyfile为
官方文档: 全局关闭自动https(防止可能的http请求跳转https)

复制代码
{
    auto_https off
}
http://auth.testweb.com {
        reverse_proxy logto:3001 {
                header_up Host {host}
                header_up X-Forwarded-Server {server_name}
                header_up X-Forwarded-Proto {scheme}
                header_up X-Forwarded-For {remote}
        }
}
http://www.testweb.com {
        reverse_proxy web:3003
}

启动容器,可以使用以下命令测试转发是否成功(需要注意这时候docker容器要打开caddy对宿主机的3000端口的监听)

复制代码
curl -v -H "Host:auth.testweb.com" http://127.0.0.1:3000

如果测试成功的话应该会返回 logto 对应的未知页面的跳转

3. 部署 Nginx

完成以上流程后,由于演示网页的80 请求和 443 请求均转发到调试linux上的3000端口 ,而ACME证书返回则是使用 http(80) 返回的,所以需要使用 nginx 对 80 请求和 443 请求进行分流。不过这里并不用单独调整 Caddyfile 特殊处理80请求,因为Caddyfile对ACME回应的路由有特殊豁免,所以只需要转发即可。

Nginx 容器内部进程监听 3000, ports: "3000:3000"则将宿主机 3000 映射进容器。作为外部请求的入口,以下是docker-compose.yml的分片

复制代码
nginx-sniff:
    image: nginx:1.27-alpine
    container_name: nginx-sniff
    restart: always
    ports:
      - "3000:3000"
    volumes:
      - ./nginx.conf:/etc/nginx/nginx.conf:ro
    depends_on:
      - caddy
    networks:
      - logto-net

Nginx也有对应的nginx.conf,本演示根据发送的请求是否是TLS请求来分流80请求和443请求到Caddy,如下

复制代码
worker_processes auto;
events {
        worker_connections 1024;
}

stream {
        ssl_preread on;
        upstream caddy_tls {
                server caddy:443;
        }
        upstream caddy_http {
                server caddy:80;
        }

        map $ssl_preread_protocol $backend {
                ~^TLS caddy_tls;
                "" caddy_http;
                default caddy_http;
        }

        server {
                listen 3000;
                proxy_connect_timeout 5s;
                proxy_timeout 30s;
                proxy_pass $backend;
        }
}

对于Nginx的分流效果的测试,可以将Caddy中的转发先调整为 respond "字符串",然后尝试向 3000端口发送带头的请求,观察返回的字符串的长度。

在完成 Nginx的部署之后,可以将Caddy改变为https(上文给出的配置)状态,尝试请求ACME证书(一定要确定域名的80端口是开放的),然后去观察调试日志。如果证书请求成功,那么完成了上述部署流程的 auth 就可以使用 3001请求了,那么控制台的效果就会如下

可以尝试随机点击一个选项查看控制台是否能查询和更改

4. 部署自制网页,提供前端访问

在控制台成功使用后,需要为 www 提供用户访问页面,提供访问页面前,先使用控制台创建网页回调,具体流程可以参考

关于 Logto 登录系统的体验以及建议-CSDN博客

本演示使用的不是实例文的React框架,使用的是后端express框架。注册完回调后,需要使用node在服务器内构建docker容器,本示例构建的Dockerfile为

复制代码
FROM node:22-alpine

WORKDIR /app

COPY package.json ./
RUN npm install

COPY testLogin.js ./

COPY testweb ./testweb

EXPOSE 3003

CMD ["node", "testweb.js"]

这里express的主代码的调用路径要和上述Dockerfile内复制进去的项目路径一致,否则会GET失败,网页框架的依赖依靠 package.json + npm install 自动构建,需要将 package.json 提前传入。

这里我使用的是 FileZilla 将文件传入服务器

构建web对应的docker-compose.yml为

复制代码
 web:
    build: ./web
    restart: unless-stopped
    expose:
      - "3003"
    volumes:
      - ./web/testweb:/app/testweb
    networks:
      - logto-net

需要注意的是,expose仅为文档标记,不会把端口映射到宿主机;同 Docker 网络内,Caddy 通过web:3003 访问服务;服务需要监听 0.0.0.0:3003,否则别的容器无法连接。

5. docker-compose.yml

完整的docker-compose.yml为

复制代码
version: '3.8'

services:
  postgres:
    image: postgres:16-alpine
    environment:
      POSTGRES_USER: logto
      POSTGRES_PASSWORD: logto
      POSTGRES_DB: logto
    volumes:
      - postgres_data:/var/lib/postgresql/data
    healthcheck:
      test: ["CMD-SHELL", "pg_isready -U logto -d logto"]
      interval: 5s
      timeout: 5s
      retries: 5
    networks:
      - logto-net
  logto:
    image: ghcr.io/logto-io/logto:latest
    depends_on:
      postgres:
        condition: service_healthy
    entrypoint: ["sh", "-c", "npm start"]
    ports:
      - "8080:3002"
    environment:
      - HOST=0.0.0.0
      - TRUST_PROXY_HEADER=1
      - DB_URL=postgres://logto:logto@postgres:5432/logto
      - ENDPOINT=https://www.testweb.com
      - ADMIN_ENDPOINT=http://127.0.0.1:8080
    networks:
      - logto-net
  caddy:
    image: caddy:latest
    volumes:
      - ./Caddyfile:/etc/caddy/Caddyfile
      - caddy_data:/data
      - caddy_config:/config
    depends_on:
      - logto
    networks:
      - logto-net

  nginx-sniff:
    image: nginx:1.27-alpine
    container_name: nginx-sniff
    restart: always
    ports:
      - "3000:3000"
    volumes:
      - ./nginx.conf:/etc/nginx/nginx.conf:ro
    depends_on:
      - caddy
    networks:
      - logto-net
  web:
    build: ./web
    restart: unless-stopped
    expose:
      - "3003"
    volumes:
      - ./web/testweb:/app/testweb
    networks:
      - logto-net
volumes:
  postgres_data:
  caddy_data:
  caddy_config:

networks:
  logto-net:
    driver: bridge

3. 后续以及补充

完成上述部署流程后,打开域名的主页应该会根据你写好的web网页跳转到你需要的网页上,本演示的效果为

点击中间的登录后,会调用Logto的登录页面,可以发现域名头为 auth

完成登录流程后,登录回调由 Logto SDK 的handleAuthRoutes捕获,完成 code 交换、会话写入后,默认重定向至首页 "/" ;可以通过 postSignInRedirectPath 修改登录成功跳转目标。

在部署完成后,可以发现主域名 www 其实可以被添加进 React 等前端框架,并部署完整的网站,服务器同时为网站服务器和后端服务器,读者如果想要分离后端与前端服务器,可以自行研究。

部署小结

用户 → 公网 80/443 → 服务器:3000 → Nginx(stream ssl_preread)
→ HTTP → Caddy:80
→ TLS → Caddy:443
Caddy:
auth.testweb.com → logto:3001
www.testweb.com → web:3003(Express)
SSH 转发:本机 ↔ 服务器 8080 ↔ logto:3002(控制台)

相关推荐
前端小D25 分钟前
JS核心底层
前端·javascript
律宏阔36 分钟前
Taro 4 项目从 npm 迁移到 pnpm 踩坑实录
前端·微信小程序·taro
用户308169987648536 分钟前
同一句话问 AI 15 次,答案 15 个:temperature 0 实测也不稳
前端
ITKEY_36 分钟前
用 Emacs Dired 实现 em:退出文件管理器后,让终端自动切换目录
linux·服务器·emacs
SameX39 分钟前
ArkTS 做 5 态数据流 —— 咖啡电量的代谢状态机怎么驱动 UI
前端
shenlongproxy39 分钟前
代理IP故障演练怎么做:注入怎么分档、恢复怎么判定
后端
知识搬运临时工39 分钟前
Kafka 发 1 GiB 数据,用户态 CPU 只花 0.016 秒,总耗时反而慢了 11%
后端·kafka
wt952740 分钟前
VMware 虚拟机磁盘扩容实战文档(Ubuntu 22.04 + LVM)
linux·后端
Dovis(誓平步青云)40 分钟前
启元 T1 Pro 人机相声搭档:语音-触摸双通道的启元机器人节奏引擎设计
开发语言·前端·javascript·microsoft·交互