Spring Boot + Vue

技术栈与工具准备

  • Spring Boot 后端框架特性与优势
  • Vue.js 前端框架特性与优势
  • 腾讯云基础服务:CVM(云服务器)、轻量应用服务器、MySQL 数据库、Redis 等
  • 辅助工具:Nginx、Docker、Jenkins(可选)

环境配置与项目打包

  • JDK、Maven/Gradle 环境安装与配置
  • Node.js 与 npm/yarn 环境安装
  • Spring Boot 项目打包(JAR 或 WAR)
  • Vue 项目打包(npm run build 生成静态文件)

腾讯云服务器初始化

  • 购买与登录云服务器(CVM 或轻量应用服务器)
  • 安全组配置(开放 80、443、后端服务端口)
  • 服务器基础环境搭建(JDK、Nginx、数据库客户端等)

后端部署方案

  • 上传 Spring Boot JAR 包至服务器
  • 通过 nohupsystemd 实现后台运行
  • 配置 HTTPS(可选:腾讯云 SSL 证书申请与 Nginx 配置)

前端部署方案

  • 上传 Vue 静态文件至服务器指定目录(如 /var/www/html
  • 配置 Nginx 反向代理与静态资源托管
  • 解决跨域问题(Nginx 代理或后端 CORS 配置)

数据库与缓存服务

  • 腾讯云 MySQL 数据库创建与连接配置
  • Redis 服务配置与 Spring Boot 集成
  • 数据备份与迁移策略

自动化部署进阶(可选)

  • Docker 容器化部署(编写 Dockerfile 与 docker-compose.yml)
  • Jenkins 持续集成与部署(Git 钩子触发构建)

监控与运维

  • 腾讯云云监控配置(CPU、内存、网络流量)
  • 日志收集与分析(ELK 或腾讯云日志服务)
  • 常见问题排查(端口占用、资源不足等)

安全优化建议

  • 服务器防火墙规则细化
  • 定期更新依赖库版本
  • 敏感信息加密(JWT、数据库密码等)

部署腾讯云后登录失败:从 401 到 Nginx 代理路径再到 BCrypt 密码排查

一、问题现象

本地运行时,Spring Boot + Vue 项目可以正常登录。

但是部署到腾讯云服务器后,登录失败,前端提示:

{ "code": "401", "msg": "认证失败,请重新登录" }

浏览器开发者工具中看到请求:

POST /api/user/login

返回:

401 Unauthorized

一开始以为是密码错误,但后面发现问题并不只是密码,而是多个部署问题叠加导致。


二、最终结论

本次登录失败主要由两个问题造成:

问题 1:Nginx 代理路径错误 前端请求 /api/user/login 但 Nginx 转发后,后端实际收到 /user/login 导致 JWT 白名单不匹配,直接返回 401

问题 2:云服务器数据库中的 admin 密码和本地不一致 Nginx 修复后,请求能进入登录逻辑 但后端返回“用户名或密码错误” 说明服务器数据库里的密码密文和本地不同

最终正确处理方式:

  1. Nginx 保留 /api 前缀转发到后端

  2. 确认后端实际端口是 1235

  3. 删除重复的 Nginx 默认配置

  4. 确认服务器数据库中的用户密码密文和本地一致


三、整体请求链路

部署后的请求链路应该是:

浏览器 | | 访问 http://服务器IP v Nginx 80 端口 | | Vue 静态页面 v 前端发起请求:/api/user/login | | Nginx 反向代理 v Spring Boot:127.0.0.1:1235/api/user/login | | 查询数据库 v MySQL:lost_found_db.user

正确情况下,后端应该收到:

/api/user/login

而不是:

/user/login


四、排查路线图

本次排查可以总结为下面这条路线:

前端提示 401 ↓ 浏览器 Network 查看登录接口 ↓ 服务器 curl 测试后端端口 ↓ 发现 8080 不通,1235 才是后端端口 ↓ 查看 jar 内 application.properties ↓ 确认 server.port=1235,数据库为 lost_found_db ↓ 测试 /user/login,返回 401 ↓ 反编译 JwtAuthenticationFilter ↓ 发现 JWT 白名单是 /api/user/login ↓ 发现 Nginx 把 /api/user/login 转成了 /user/login ↓ 修改 Nginx proxy_pass,保留 /api 前缀 ↓ 401 变成“用户名或密码错误” ↓ 说明请求已进入登录逻辑 ↓ 检查 MySQL user 表 ↓ 发现服务器数据库密码密文和本地不一致


五、第一层问题:后端实际端口不是 8080

最开始以为后端跑在 "8080",于是测试:

curl -i -X POST http://127.0.0.1:8080/user/login

结果:

Connection refused

说明 "8080" 没有服务。

查看 Java 进程:

ps -ef | grep java

看到后端是这样启动的:

java -jar app.jar --spring.profiles.active=prod --spring.jpa.hibernate.ddl-auto=update

查看 jar 中的配置:

cd /opt/app/backend unzip -p app.jar BOOT-INF/classes/application.properties

发现:

server.port=1235

所以后端实际端口是:

1235

因此后续测试应使用:

curl -i -X POST http://127.0.0.1:1235/api/user/login

而不是:

curl -i -X POST http://127.0.0.1:8080/api/user/login


六、第二层问题:以为加载 prod,实际只有 application.properties

启动命令中写了:

--spring.profiles.active=prod

所以一开始以为项目加载的是:

application-prod.yml

或者:

application-prod.properties

但查看 jar 包:

jar tf app.jar | grep application

结果只有:

BOOT-INF/classes/application.properties

也就是说,jar 里面没有 prod 配置文件。

实际使用的配置是:

spring.datasource.url=jdbc:mysql://localhost:3306/lost_found_db?useUnicode=true&characterEncoding=UTF-8&serverTimezone=Asia/Shanghai spring.datasource.username=root spring.datasource.password=123456 server.port=1235

这里要注意:

--spring.profiles.active=prod

并不会凭空创建 prod 配置。

如果 jar 中没有:

application-prod.properties

或:

application-prod.yml

那就不会加载 prod 专属配置。


七、第三层问题:Nginx 转发时去掉了 /api 前缀

前端请求是:

POST /api/user/login

最开始 Nginx 配置为:

location /api/ { proxy_pass http://127.0.0.1:1235/; }

这个配置会导致:

前端请求:/api/user/login 后端收到:/user/login

也就是说,Nginx 把 "/api" 前缀去掉了。

这在普通接口里不一定有问题,但本项目的 JWT 过滤器白名单写的是:

/api/user/login

所以后端收到 "/user/login" 后,并不会认为这是公开接口,直接返回:

{ "code": "401", "msg": "认证失败,请重新登录" }


八、为什么确定是 JWT 过滤器拦截?

反编译 "UserService.login()" 后可以看到:

如果密码错误,抛出:用户名或密码错误

但是接口实际返回的是:

认证失败,请重新登录

这说明请求没有进入登录方法,而是被过滤器提前拦截了。

继续反编译 "JwtAuthenticationFilter":

cd /tmp/app javap -classpath BOOT-INF/classes -p -c org.example.springboot.config.JwtAuthenticationFilter

可以看到过滤器中有类似逻辑:

response.setStatus(401); response.setContentType("application/json;charset=UTF-8"); response.getWriter().write("{"code":"401","msg":"认证失败,请重新登录"}");

并且公开路径包括:

/api/user/login /api/user/register /api/user/forget /api/user/add

关键点在这里:

JWT 白名单:/api/user/login Nginx 转发后:/user/login 结果:白名单不匹配,返回 401


九、错误配置与正确配置对比

错误配置

location /api/ { proxy_pass http://127.0.0.1:1235/; }

路径变化:

/api/user/login → /user/login

结果:

JWT 白名单不匹配 返回 401:认证失败,请重新登录


正确配置

location /api/ { proxy_pass http://127.0.0.1:1235/api/;

proxy_http_version 1.1;
​
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;
​
proxy_set_header Authorization $http_authorization;
proxy_set_header token $http_token;
proxy_set_header satoken $http_satoken;

}

路径变化:

/api/user/login → /api/user/login

结果:

JWT 白名单匹配 请求进入 UserController.login()


十、Nginx 配置重复问题

排查时通过:

sudo nginx -T | grep -A25 -B5 "location /api"

发现有两个配置文件里都写了 "/api/":

/etc/nginx/conf.d/myapp.conf /etc/nginx/sites-enabled/default

这会导致自己改了一个配置,但实际生效的可能是另一个。

解决方法:

sudo rm -f /etc/nginx/sites-enabled/default sudo nginx -t sudo systemctl reload nginx

再确认:

sudo nginx -T | grep -A25 -B5 "location /api"

确保只剩下一个 "/api/" 配置,并且里面是:

proxy_pass http://127.0.0.1:1235/api/;


十一、Nginx 修复前后的接口返回对比

修复前

测试:

curl -i -X POST http://127.0.0.1/api/user/login \ -H "Content-Type: application/json" \ -d '{"username":"admin","password":"123456"}'

返回:

{ "code": "401", "msg": "认证失败,请重新登录" }

说明:

还没进入登录逻辑 被 JWT 过滤器拦截了


修复后

再次测试:

curl -i -X POST http://127.0.0.1/api/user/login \ -H "Content-Type: application/json" \ -d '{"username":"admin","password":"123456"}'

返回:

{ "code": "-1", "msg": "用户名或密码错误" }

说明:

Nginx 路径已经正确 JWT 没有再拦截登录接口 请求已经进入 UserService.login()

这个返回虽然还是登录失败,但已经是业务层失败,不再是部署路径问题。


十二、第四层问题:服务器数据库密码和本地不一致

进入服务器 MySQL:

mysql -u root -p

查看数据库:

USE lost_found_db; SHOW TABLES; SELECT * FROM user LIMIT 10;

用户表中存在:

user

admin 用户也存在:

username = admin password = $2a$10$... status = 1

其中 password 是 BCrypt 密文。

这说明项目不是明文密码登录,而是类似这样校验:

passwordEncoder.matches(前端输入的明文密码, 数据库中的BCrypt密文)

所以不能直接执行:

UPDATE user SET password = '123456' WHERE username = 'admin';

这样会导致永远登录失败。


十三、如何修复数据库密码问题

推荐方式是:从本地数据库复制 admin 的 BCrypt 密文到云服务器数据库。

  1. 本地数据库查询 admin 密文

USE lost_found_db;

SELECT username, password FROM user WHERE username = 'admin';

复制本地 "password" 字段。


  1. 云服务器数据库更新 admin 密码

USE lost_found_db;

UPDATE user SET password = '本地admin用户的BCrypt密文' WHERE username = 'admin';


  1. 重新测试登录

curl -i -X POST http://127.0.0.1/api/user/login \ -H "Content-Type: application/json" \ -d '{"username":"admin","password":"123456"}'

如果返回用户信息和 token,说明登录成功。


十四、最终推荐 Nginx 配置

完整示例:

server { listen 80; server_name 你的服务器IP或域名;

root /opt/app/frontend;
index index.html;
​
location / {
    try_files $uri $uri/ /index.html;
}
​
location /api/ {
    proxy_pass http://127.0.0.1:1235/api/;
​
    proxy_http_version 1.1;
​
    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;
​
    proxy_set_header Authorization $http_authorization;
    proxy_set_header token $http_token;
    proxy_set_header satoken $http_satoken;
}

}

修改后执行:

sudo nginx -t sudo systemctl reload nginx


十五、快速定位类似问题的方法

如果部署后登录失败,可以按下面顺序排查。

  1. 看前端请求地址

浏览器打开:

F12 → Network

确认登录接口:

/api/user/login


  1. 直接请求后端

curl -i -X POST http://127.0.0.1:1235/api/user/login \ -H "Content-Type: application/json" \ -d '{"username":"admin","password":"123456"}'

如果返回:

{ "code": "-1", "msg": "用户名或密码错误" }

说明后端接口通了,问题在账号密码或数据库。

如果返回:

{ "code": "401", "msg": "认证失败,请重新登录" }

说明可能被 JWT 或 Security 拦截。


  1. 再请求 Nginx

curl -i -X POST http://127.0.0.1/api/user/login \ -H "Content-Type: application/json" \ -d '{"username":"admin","password":"123456"}'

对比两个结果:

直连 1235 成功进入业务逻辑 走 Nginx 返回 401

说明 Nginx 转发路径有问题


  1. 查看 Nginx 生效配置

sudo nginx -T | grep -A25 -B5 "location /api"

确认:

proxy_pass http://127.0.0.1:1235/api/;


  1. 查看 jar 中配置

jar tf app.jar | grep application unzip -p app.jar BOOT-INF/classes/application.properties

确认:

server.port spring.datasource.url spring.datasource.username spring.datasource.password


  1. 查数据库用户

USE lost_found_db;

SELECT username, password, status FROM user WHERE username = 'admin';

确认:

用户存在 密码是 BCrypt 密文 状态正常


十六、一张表总结问题

阶段| 现象| 原因| 解决方式 后端端口| 访问 8080 失败| 后端实际端口是 1235| 查看 application.properties,改 Nginx 端口 配置文件| 以为加载 prod| jar 中没有 application-prod 配置| 查看 jar tf app.jar,确认真实配置 JWT 认证| 登录返回 401| Nginx 去掉 /api,白名单不匹配| proxy_pass 改为 "http://127.0.0.1:1235/api/" Nginx 配置| 改了但不生效| 存在多个 Nginx 配置| 删除 sites-enabled/default 密码校验| 返回用户名或密码错误| 云服务器数据库密码和本地不同| 同步本地 BCrypt 密文或重新导入数据库 数据库密码| 明文改 123456 无效| 项目使用 BCrypt| 必须写入 BCrypt 密文


十七、这次踩坑的核心教训

  1. 401 不一定是密码错

如果返回:

{ "code": "401", "msg": "认证失败,请重新登录" }

优先怀疑:

JWT 过滤器 Spring Security 请求头 token Nginx 路径转发

而不是直接怀疑密码。


  1. “用户名或密码错误”才是真的进入登录逻辑

如果返回:

{ "code": "-1", "msg": "用户名或密码错误" }

说明请求已经到达登录业务方法。

这时才应该去查:

数据库账号 数据库密码密文 用户状态 密码加密方式


  1. Nginx 的 proxy_pass 路径规则很容易踩坑

这两个配置非常像,但效果完全不同:

proxy_pass http://127.0.0.1:1235/;

会导致:

/api/user/login → /user/login

而:

proxy_pass http://127.0.0.1:1235/api/;

会导致:

/api/user/login → /api/user/login

如果后端 JWT 白名单写的是 "/api/user/login",就必须保留 "/api"。


  1. 云服务器数据库一定要单独检查

本地可以登录,不代表服务器可以登录。

部署后至少要检查:

SELECT username, password, status FROM user WHERE username = 'admin';

确认云服务器数据库中的用户数据和本地一致。


十八、最终结论

本次 Spring Boot + Vue 项目部署到腾讯云后无法登录,最终定位为:

Nginx 转发路径错误 + JWT 白名单路径不匹配 + 服务器数据库密码密文与本地不一致

修复顺序是:

  1. 确认后端真实端口是 1235

  2. 确认后端真实登录接口是 /api/user/login

  3. 修改 Nginx,让它保留 /api 前缀

  4. 删除重复 Nginx 默认配置

  5. 确认接口从 401 变成“用户名或密码错误”

  6. 同步本地数据库中的 BCrypt 密码密文

  7. 登录成功

这个问题的关键不是“密码对不对”,而是要先判断请求有没有真正进入登录逻辑。

只要能区分:

401 认证失败,请重新登录

和:

用户名或密码错误

排查方向就会清晰很多。

Logo

openEuler 是由开放原子开源基金会孵化的全场景开源操作系统项目,面向数字基础设施四大核心场景(服务器、云计算、边缘计算、嵌入式),全面支持 ARM、x86、RISC-V、loongArch、PowerPC、SW-64 等多样性计算架构

更多推荐