三、node,mongoose实现用户登录token生成、鉴权

用户Schema和密码加密

        首先,我们使用Mongoose定义用户数据模型。这里包含用户名(username)和密码(password),并且在密码字段上设置了一个预保存钩子(pre-save hook),用于在存储到数据库前对其进行bcrypt加密,使用bcryptjs工具。

const mongoose = require('mongoose');
const bcrypt = require('bcryptjs');

// 定义用户Schema
const userSchema = new mongoose.Schema({
  name: { type: String, required: true },
  email: { type: String, unique: true, required: true },
  password: { type: String, required: true },
  // 其他字段...
});

// 预保存钩子:在保存用户前对密码进行bcrypt哈希处理
userSchema.pre('save', async function (next) {
  if (!this.isModified('password')) return next();
  this.password = await bcrypt.hash(this.password, 10); // 加密强度为10的bcrypt算法
  next();
});

// 创建并注册User Model
const User = mongoose.model('User', userSchema);
用户登录功能

        创建一个处理用户登录请求的API端点,验证email和密码是否匹配,并在成功时生成JWT Token,如果用户不存在则创建一个新用户。

 文件:routes/user.js

// usersRoutes.js
const express = require("express");
const bcrypt = require("bcryptjs");
const jwt = require("jsonwebtoken");
const secret = 'mySuperSecretKeyForJwtSigning';
const { User } = require("../model/main"); // 确保路径正确指向db.js文件

const router = express.Router();

// 定义登录路由
router.post("/login", async (req, res) => {
  try {
    const { email, password, name } = req.body;
    // 先尝试查询数据库获取对应用户名的用户
    let user = await User.findOne({ email });
    if (!user) {
      // 如果用户不存在,则创建新用户并保存到数据库
      user = new User({ email, password, name });
      await user.save();
    } else {
      // 对于已存在的用户,验证密码是否匹配
      if (!await bcrypt.compare(password, user.password)) {
        return res.status(401).json({ error: 'Invalid credentials' });
      }
    }
    // 密码正确或新用户已创建,生成Token
    const token = jwt.sign({ userId: user._id }, secret, { expiresIn: "1h" }); // 设置过期时间为1小时
    // 将Token和其他用户信息返回给客户端
    res.json({ token, name: user.name || null, id: user._id, email: user.email });
  } catch (error) {
    console.error(error);
    res.status(500).json({ error: "Server error" });
  }
});

// 查询单个用户或所有用户接口
router.get("/:id?", async (req, res) => {
  try {
    if (req.params.id) {
      const userId = req.params.id;
      const user = await User.findById(userId);
      if (!user) {
        return res.status(404).json({ message: "User not found." });
      }
      res.json(user);
    } else {
      const users = await User.find();
      res.json(users);
    }
  } catch (error) {
    console.error("Error fetching user(s):", error);
    res
      .status(500)
      .json({ error: "An error occurred while fetching the user(s)." });
  }
});

module.exports = router;
JWT Token生成说明

        这里利用jsonwebtoken库根据用户ID生成一个签名的Token,这个Token内包含了用户的唯一标识符userId。设定一定的过期时间以提高安全性。

const jwt = require('jsonwebtoken');
const secret = 'mySuperSecretKeyForJwtSigning' // process.env.JWT_SECRET 这个secret应从环境变量中获取,确保安全

// 假设上述login函数内部
const token = jwt.sign({ userId: user._id }, secret, { expiresIn: '1h' }); // 过期时间为1小时
JWT鉴权中间件

        创建一个中间件,该中间件会在每次保护路由被访问时执行。其作用是从请求头中提取Token,并解码验证Token的有效性。如果有效,则将解码后的用户信息附加到请求对象上以便后续操作。

文件:auth.js

const jwt = require("jsonwebtoken");
const secret = "mySuperSecretKeyForJwtSigning";

const authMiddleware = (req, res, next) => {
  const noAuthPaths = ["/api/users/login"];
  if (noAuthPaths.some((path) => req.path.startsWith(path))) {
    return next(); // 路径在白名单内,直接进入下一个中间件或路由处理函数
  }
  const authorizationHeader = req.headers["authorization"];
  // 检查请求头中是否存在Authorization头,并从中提取出Bearer后面的Token部分
  if (!authorizationHeader || !authorizationHeader.startsWith("Bearer ")) {
    return res.status(401).json({ error: "Unauthorized" });
  }
  const token = authorizationHeader.split(" ")[1];
  try {
    // 解码并验证Token
    const decoded = jwt.verify(token, secret);
    // 将解码得到的用户信息添加到请求对象上
    req.user = decoded;
    next();
  } catch (err) {
    return res.status(401).json({ error: "Unauthorized" });
  }
};

module.exports = authMiddleware;
在应用中全局启用鉴权中间件 
const authMiddleware = require('./auth');
app.use(authMiddleware)

server.js文件完整代码 

// server.js
const express = require('express');
const bodyParser = require('body-parser');
const cors = require('cors');
const { connect } = require('./db'); // 确保路径正确指向db.js文件
const authMiddleware = require('./auth');

const app = express();
app.use(cors());
app.use(bodyParser.json());
app.use(authMiddleware)

// 在服务器启动前先连接数据库
async function startServer() {
  try {
    await connect();
    console.log('Connected to MongoDB');
    // 挂载用户路由到 /api/users
    const usersRoutes = require('./routes/user');
    app.use('/api/users', usersRoutes);
    // 启动服务器
    const PORT = process.env.PORT || 3000;
    app.listen(PORT, () => {
      console.log(`Server is running on port ${PORT}`);
    });
  } catch (error) {
    console.error('Error connecting to MongoDB:', error);
  }
}

startServer();
前端调用展示:

        

        以上代码展示了如何在Node.js与Mongoose配合下实现用户登录及基于JWT的Token鉴权机制的基本流程。实际项目中还应考虑更多细节,例如异常处理、Token刷新策略、Token黑名单管理等。 

  • 17
    点赞
  • 6
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

陨石猎人

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值