记一次 Spring Boot + Nginx 登录踩坑实录:从 404 → 502 → 400 → 密码错误,最终 200

一、背景

最近在做一个 Spring Boot 2.x + Vue 的前后端分离项目,部署时使用 Nginx 1.20.2 作为反向代理。前端页面能正常打开,但点击登录按钮后没有任何反应,没有跳转,没有提示,像死了一样。打开浏览器 F12 查看网络请求,发现登录接口返回了 404。于是开始了漫长的踩坑之旅。

二、环境说明

组件版本
Spring Boot2.x
JDK8
Nginx1.20.2
前端Vue 2.x
数据库MySQL
操作系统Windows

三、踩坑实录

第一阶段:Nginx 路径不匹配 → 404

现象:F12 网络面板显示请求 POST /api/employee/login 返回 404。

查看 Nginx access.log,发现请求确实到了 Nginx:

127.0.0.1 - - [08/Aug/2026:21:04:34 +0800] "POST /api/employee/login HTTP/1.1" 404 96

排查过程

后端接口路径为:

@RestController
@RequestMapping("/admin/employee")
public class EmployeeController {
    @PostMapping("/login")
    public Result<EmployeeLoginVO> login(@RequestBody EmployeeLoginDTO login) {
        // ...
    }
}

即完整路径为 /admin/employee/login

而 Nginx 配置中:

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

由于 proxy_pass 末尾带 //api/employee/login 被转发成了 /employee/login,少了一层 /admin,导致后端匹配不到接口。

解决方案

location /api/ {
    proxy_pass http://127.0.0.1:8080/admin/;  # 加上 /admin/
    proxy_set_header Host $host;
    proxy_set_header X-Real-IP $remote_addr;
}

第二阶段:配置变量错误 → 502

现象:F12 网络面板中偶尔出现 502 Bad Gateway。

排查过程:配置中有一行 proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;,但由于 Windows 记事本字体渲染问题,下划线在屏幕显示上容易被误认为空格,导致 Nginx 解析时无法识别该变量,从而报错。

解决方案:删掉 X-Forwarded-For 相关配置(非必须),或者确保变量名拼写完全正确。精简后的配置:

location /api/ {
    proxy_pass http://127.0.0.1:8080/admin/;
    proxy_set_header Host $host;
    proxy_set_header X-Real-IP $remote_addr;
}

提示X-Forwarded-For 用于获取客户端真实 IP,在登录功能中非必须,删除不影响正常使用。


第三阶段:浏览器直接访问 → 400 Bad Request

现象:为了验证后端是否正常,直接在浏览器地址栏访问 http://127.0.0.1:8080/admin/employee/login,返回 Spring Boot 默认的 400 Bad Request 白页。

排查过程:后端接口使用 @PostMapping("/login"),只接受 POST 请求。浏览器地址栏访问默认是 GET 请求,因此返回 400。

解决方案:使用 Postman/ApiFox 等工具发送 POST 请求进行测试,或者确保前端登录请求为 POST 方式。

提示:F12 网络面板中可以看到前端请求的方法类型,确认是 POST 即可。


第四阶段:转发成功但密码错误

现象:Nginx 转发成功后,登录接口返回 200,但响应内容为:

{
  "code": 0,
  "msg": "密码错误",
  "data": null
}

排查过程:查看后端 EmployeeServiceImpl.login 方法:

public Employee login(EmployeeLoginDTO employeeLoginDTO) {
    String username = employeeLoginDTO.getUsername();
    String password = employeeLoginDTO.getPassword();
    Employee employee = employeeMapper.getByUsername(username);
    if (employee == null) {
        throw new AccountNotFoundException("账号不存在");
    }
    // 密码比对 — 先加密再比对
    password = DigestUtils.md5DigestAsHex(password.getBytes());
    if (!password.equals(employee.getPassword())) {
        throw new PasswordErrorException("密码错误");
    }
    if (employee.getStatus() == StatusConstant.DISABLE) {
        throw new AccountLockedException("账号被锁定");
    }
    return employee;
}

前端传入的明文密码会被 MD5 加密后再与数据库比对。查看数据库 employee 表,发现密码字段存的是明文 123456,而 MD5 加密后的 123456e10adc3949ba59abbe56e057f20f883e,二者不匹配。

原因分析:项目初始化时导入的 SQL 脚本直接插入了明文密码,但代码中一直存在 MD5 加密比对逻辑,导致历史数据与代码逻辑不一致。

解决方案:将数据库中密码字段更新为 MD5 密文:

UPDATE employee SET password = 'e10adc3949ba59abbe56e057f20f883e' WHERE username = 'admin';

更新后,使用明文密码 123456 登录成功。


第五阶段:一劳永逸的解决方式

为了让后续新增的员工不再出现同样的问题,修改新增员工代码,存入数据库前自动加密:

employee.setPassword(DigestUtils.md5DigestAsHex(PasswordConstant.DEFAULT_PASSWORD.getBytes()));

其中 PasswordConstant.DEFAULT_PASSWORD 定义为:

public static final String DEFAULT_PASSWORD = "123456";

这样所有新增员工都会自动加密存储,登录时加密比对即可匹配。

四、最终 Nginx 配置

server {
    listen 80;
    server_name localhost;

    location / {
        root html/sky;
        index index.html index.htm;
    }

    error_page 500 502 503 504 /50x.html;
    location = /50x.html {
        root html;
    }

    # 反向代理 — 管理端
    location /api/ {
        proxy_pass http://127.0.0.1:8080/admin/;
        proxy_set_header Host $host;
        proxy_set_header X-Real-IP $remote_addr;
    }

    # 反向代理 — 用户端
    location /user/ {
        proxy_pass http://webservers/user/;
    }

    # WebSocket
    location /ws/ {
        proxy_pass http://webservers/ws/;
        proxy_http_version 1.1;
        proxy_set_header Upgrade $http_upgrade;
        proxy_set_header Connection "upgrade";
        proxy_read_timeout 3600s;
    }
}

五、如何快速排查类似问题

如果以后再遇到前端请求无响应的情况,按这个顺序查:

  1. 看 F12 网络面板:确认请求 URL、状态码、响应内容。
  2. 看 Nginx access.log:确认请求是否到达 Nginx。
  3. 看后端控制台:在接口入口加 System.out.println("请求进来了"),确认请求是否进入后端。
  4. 直接测后端接口:用 Postman 发请求,绕过 Nginx,确认后端本身是否正常。

六、总结

这次登录踩坑经历,本质上是三个独立问题的叠加:

  1. Nginx 路径转发问题proxy_pass 路径要匹配后端接口的完整路径。
  2. 数据库历史数据问题:SQL 脚本存的明文与代码加密逻辑不一致。
  3. 配置文件编码/显示问题:下划线在部分编辑器中显示异常,影响配置解析。

排查时冷静分析,从 F12 出发,逐层向上游(Nginx → 后端 → 数据库)推进,总能定位到根因。


希望这篇踩坑记录能帮到遇到类似问题的朋友。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值