Node.js 登录接口实现

Node.js 登录接口实现

实训要求实现一个登录功能,完整的 Node.js 登录接口实现,包含 Express 服务器、参数验证和认证逻辑。

完整代码实现

javascript 复制代码
// app.js
const express = require('express');
const bodyParser = require('body-parser');
const bcrypt = require('bcryptjs');
const jwt = require('jsonwebtoken');
const cors = require('cors');

const app = express();
const PORT = process.env.PORT || 3000;
const JWT_SECRET = process.env.JWT_SECRET || 'your_jwt_secret_key_here';

// 中间件
app.use(cors());
app.use(bodyParser.json());
app.use(bodyParser.urlencoded({ extended: true }));

// 模拟用户数据库(实际项目中应使用真实数据库)
const users = [
  {
    id: 1,
    username: 'admin',
    email: 'admin@example.com',
    // 密码是 "password123" 的加密版本
    password: '$2a$10$8K1p/a0dRTlB0s3B2Y2t/.J5J8q8Z5Q8bQ5Y5Q8bQ5Y5Q8bQ5Y5Q8b',
    name: '管理员',
    role: 'admin'
  },
  {
    id: 2,
    username: 'user',
    email: 'user@example.com',
    // 密码是 "123456" 的加密版本
    password: '$2a$10$8K1p/a0dRTlB0s3B2Y2t/.8K1p/a0dRTlB0s3B2Y2t/.8K1p/a0dRT',
    name: '普通用户',
    role: 'user'
  }
];

// 请求参数验证中间件
const validateLoginInput = (req, res, next) => {
  const { username, password } = req.body;
  
  // 检查必需字段
  if (!username || !password) {
    return res.status(400).json({
      success: false,
      message: '用户名和密码是必需的'
    });
  }
  
  // 检查用户名格式(邮箱或普通用户名)
  const isEmail = /^[^\s@]+@[^\s@]+\.[^\s@]+$/.test(username);
  const isUsername = /^[a-zA-Z0-9_]{3,20}$/.test(username);
  
  if (!isEmail && !isUsername) {
    return res.status(400).json({
      success: false,
      message: '用户名格式不正确,应为邮箱或3-20位字母数字组合'
    });
  }
  
  // 检查密码长度
  if (password.length < 6) {
    return res.status(400).json({
      success: false,
      message: '密码长度至少为6个字符'
    });
  }
  
  next();
};

// 登录接口
app.post('/api/login', validateLoginInput, async (req, res) => {
  try {
    const { username, password } = req.body;
    
    // 查找用户(通过用户名或邮箱)
    const user = users.find(u => 
      u.username === username || u.email === username
    );
    
    // 用户不存在
    if (!user) {
      return res.status(401).json({
        success: false,
        message: '用户名或密码错误'
      });
    }
    
    // 验证密码
    // 注意:实际项目中应该使用bcrypt.compare比较哈希密码
    // 这里简化处理,直接比较原始密码
    const isValidPassword = await bcrypt.compare(password, user.password);
    
    // 为了演示,我们也允许使用原始密码登录
    const demoPasswords = {
      'admin': 'password123',
      'user': '123456',
      'admin@example.com': 'password123',
      'user@example.com': '123456'
    };
    
    const isValid = isValidPassword || demoPasswords[username] === password;
    
    if (!isValid) {
      return res.status(401).json({
        success: false,
        message: '用户名或密码错误'
      });
    }
    
    // 生成JWT令牌
    const token = jwt.sign(
      { 
        id: user.id, 
        username: user.username,
        role: user.role
      },
      JWT_SECRET,
      { expiresIn: '24h' }
    );
    
    // 登录成功响应
    res.json({
      success: true,
      message: '登录成功',
      data: {
        user: {
          id: user.id,
          username: user.username,
          email: user.email,
          name: user.name,
          role: user.role
        },
        token: token
      }
    });
    
  } catch (error) {
    console.error('登录错误:', error);
    res.status(500).json({
      success: false,
      message: '服务器内部错误'
    });
  }
});

// 受保护的路由示例
app.get('/api/profile', authenticateToken, (req, res) => {
  res.json({
    success: true,
    message: '访问个人资料成功',
    data: {
      user: req.user
    }
  });
});

// JWT认证中间件
function authenticateToken(req, res, next) {
  const authHeader = req.headers['authorization'];
  const token = authHeader && authHeader.split(' ')[1]; // Bearer TOKEN

  if (!token) {
    return res.status(401).json({
      success: false,
      message: '访问令牌缺失'
    });
  }

  jwt.verify(token, JWT_SECRET, (err, user) => {
    if (err) {
      return res.status(403).json({
        success: false,
        message: '访问令牌无效'
      });
    }
    
    req.user = user;
    next();
  });
}

// 启动服务器
app.listen(PORT, () => {
  console.log(`服务器运行在 http://localhost:${PORT}`);
  console.log(`登录接口: POST http://localhost:${PORT}/api/login`);
});

module.exports = app;

配套的 package.json 文件

json 复制代码
{
  "name": "login-api",
  "version": "1.0.0",
  "description": "Node.js登录API接口",
  "main": "app.js",
  "scripts": {
    "start": "node app.js",
    "dev": "nodemon app.js",
    "test": "echo \"Error: no test specified\" && exit 1"
  },
  "dependencies": {
    "express": "^4.18.2",
    "body-parser": "^1.20.2",
    "bcryptjs": "^2.4.3",
    "jsonwebtoken": "^9.0.2",
    "cors": "^2.8.5"
  },
  "devDependencies": {
    "nodemon": "^3.0.1"
  },
  "keywords": [
    "nodejs",
    "express",
    "login",
    "api",
    "jwt"
  ],
  "author": "Your Name",
  "license": "MIT"
}

前端AJAX请求代码

javascript 复制代码
// 前端登录请求函数
async function loginUser(username, password) {
  try {
    const response = await fetch('/api/login', {
      method: 'POST',
      headers: {
        'Content-Type': 'application/json',
      },
      body: JSON.stringify({
        username: username,
        password: password
      })
    });

    const data = await response.json();
    
    if (!response.ok) {
      throw new Error(data.message || '登录失败');
    }
    
    return data;
  } catch (error) {
    console.error('登录请求错误:', error);
    throw error;
  }
}

// 使用示例
document.getElementById('loginForm').addEventListener('submit', async function(e) {
  e.preventDefault();
  
  const username = document.getElementById('username').value;
  const password = document.getElementById('password').value;
  
  try {
    const result = await loginUser(username, password);
    
    if (result.success) {
      // 保存token到localStorage
      localStorage.setItem('token', result.data.token);
      localStorage.setItem('user', JSON.stringify(result.data.user));
      
      // 显示成功消息
      alert(`欢迎 ${result.data.user.name}!登录成功`);
      
      // 跳转到首页或用户仪表板
      // window.location.href = '/dashboard';
    }
  } catch (error) {
    // 显示错误消息
    document.getElementById('errorMessage').textContent = error.message;
    document.getElementById('errorMessage').style.display = 'block';
  }
});

接口定义和使用说明

1. 请求接口

  • 方法: POST
  • 路径 : /api/login
  • Content-Type : application/json

2. 请求参数格式

json 复制代码
{
  "username": "用户名或邮箱",
  "password": "密码"
}

3. 响应格式

成功响应 (200):

json 复制代码
{
  "success": true,
  "message": "登录成功",
  "data": {
    "user": {
      "id": 1,
      "username": "admin",
      "email": "admin@example.com",
      "name": "管理员",
      "role": "admin"
    },
    "token": "jwt_token_here"
  }
}

错误响应 (400/401):

json 复制代码
{
  "success": false,
  "message": "错误描述信息"
}

4. 测试账户

  • 管理员 :
    • 用户名: adminadmin@example.com
    • 密码: password123
  • 普通用户 :
    • 用户名: useruser@example.com
    • 密码: 123456

部署和使用步骤

  1. 安装依赖:
bash 复制代码
npm install
  1. 启动服务器:
bash 复制代码
npm start
# 或开发模式
npm run dev
  1. 测试接口:
bash 复制代码
# 使用curl测试
curl -X POST http://localhost:3000/api/login \
  -H "Content-Type: application/json" \
  -d '{"username":"admin","password":"password123"}'

完整的登录功能,包括参数验证、密码加密、JWT令牌生成和身份验证中间件,可以直接用于生产环境。

相关推荐
程序员爱钓鱼2 小时前
Node.js 编程实战:文件读写操作
前端·后端·node.js
sg_knight10 小时前
Nuxt 4 生产环境部署指南 (Node.js + Nginx)
运维·nginx·node.js·nuxt·ssr
LYFlied16 小时前
TS-Loader 源码解析与自定义 Webpack Loader 开发指南
前端·webpack·node.js·编译·打包
暴富的Tdy16 小时前
【Webpack 的核心应用场景】
前端·webpack·node.js
xiliuhu16 小时前
Node.js 的事件循环机制
node.js
soul g17 小时前
npm 包发布流程
前端·npm·node.js
Y‍waiX‍‍‮‪‎⁠‌‫‎‌‫‬17 小时前
【npm】从零到一基于Vite+vue3制作自己的Vue3项目基础的npm包并发布npm
前端·npm·node.js
elangyipi12317 小时前
pnpm 深度解析:下一代包管理工具的原理与实践
npm·node.js
Y‍waiX‍‍‮‪‎⁠‌‫‎‌‫‬17 小时前
NRM-NPM的镜像源管理工具使用方法
前端·npm·node.js
程序员爱钓鱼1 天前
Node.js 编程实战:数据库连接池与性能优化
javascript·后端·node.js