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令牌生成和身份验证中间件,可以直接用于生产环境。

相关推荐
aidou13147 小时前
Visual Studio Code(VS Code)安装步骤
vscode·npm·node.js·环境变量
止观止8 小时前
告别 require!TypeScript 5.9 与 Node.js 20+ 的 ESM 互操作指南
javascript·typescript·node.js
一只专注api接口开发的技术猿9 小时前
淘宝商品详情API的流量控制与熔断机制:保障系统稳定性的后端设计
大数据·数据结构·数据库·架构·node.js
天远数科12 小时前
天远车辆过户查询API集成指南:Node.js 全栈视角下的二手车数据挖掘
大数据·数据挖掘·node.js·vim
全栈小514 小时前
【前端】win11操作系统安装完最新版本的NodeJs运行npm install报错,提示在此系统上禁止运行脚本
前端·npm·node.js
莫有杯子的龙潭峡谷1 天前
在 Windows 系统上安装 OpenClaw
人工智能·node.js·安装教程·openclaw
朝朝暮暮an1 天前
Node.js-第一天学习内容
node.js
lichenyang4531 天前
Node.js AI 开发入门 - 完整学习笔记
人工智能·学习·node.js
岁岁种桃花儿1 天前
NodeJs从入门到上天:什么是Node.js
前端·node.js
一心赚狗粮的宇叔2 天前
VScode常用扩展包&Node.js安装及npm包安装
vscode·npm·node.js·web