You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Node.js认证与URL短链接融合应用开发求助

Node.js 登录认证 + URL短链接整合实现方案

你需要的是整合登录认证与URL短链接功能的Node.js应用,启动后先进入登录页,认证通过后才能使用短链接功能。以下是修复并整合后的完整代码,以及关键修改说明:

完整代码

if (process.env.NODE_ENV !== "production") {
  require("dotenv").config();
}

const express = require("express");
const app = express();
const bcrypt = require("bcrypt");
const passport = require("passport");
const initializePassport = require("./passport");
const flash = require("express-flash");
const session = require("express-session");
const mongoose = require("mongoose");
const ShortUrl = require("./models/shortUrl");

// 连接MongoDB
mongoose.connect("mongodb://localhost/urlShortener", {
  useNewUrlParser: true,
  useUnifiedTopology: true,
});

const users = [];

// 初始化Passport
initializePassport(
  passport,
  (email) => users.find((user) => user.email === email),
  (id) => users.find((user) => user.id === id)
);

// 中间件配置
app.set("view engine", "ejs");
app.use(express.urlencoded({ extended: false }));
app.use(flash());
app.use(
  session({
    secret: process.env.SESSION_SECRET,
    resave: false,
    saveUninitialized: false,
  })
);
app.use(passport.initialize());
app.use(passport.session());

// 登录验证中间件:未登录用户无法访问受保护路由
function checkAuthenticated(req, res, next) {
  if (req.isAuthenticated()) {
    return next();
  }
  res.redirect("/login");
}

// 路由配置
// 根路由重定向到登录页,启动后直接加载登录
app.get("/", (req, res) => {
  res.redirect("/login");
});

// 登录页
app.get("/login", (req, res) => {
  res.render("login.ejs");
});

// 注册页
app.get("/register", (req, res) => {
  res.render("register.ejs");
});

// 登录提交
app.post(
  "/login",
  passport.authenticate("local", {
    successRedirect: "/index",
    failureRedirect: "/login",
    failureFlash: true,
  })
);

// 注册提交
app.post("/register", async (req, res) => {
  try {
    const hashedPassword = await bcrypt.hash(req.body.password, 10);
    users.push({
      id: Date.now().toString(),
      name: req.body.name,
      email: req.body.email,
      password: hashedPassword,
    });
    res.redirect("/login");
  } catch (e) {
    console.log(e);
    res.redirect("/register");
  }
});

// 登出(可选添加)
app.post("/logout", (req, res) => {
  req.logout((err) => {
    if (err) return next(err);
    res.redirect("/login");
  });
});

// 受保护的首页(URL短链接功能页)
app.get("/index", checkAuthenticated, async (req, res) => {
  const shortUrls = await ShortUrl.find();
  res.render("index", { shortUrls: shortUrls, user: req.user });
});

// 创建短链接
app.post("/shortUrls", checkAuthenticated, async (req, res) => {
  await ShortUrl.create({ full: req.body.fullUrl });
  res.redirect("/index");
});

// 短链接跳转 + 访问计数
app.get("/:shortUrl", async (req, res) => {
  const shortUrl = await ShortUrl.findOne({ short: req.params.shortUrl });
  if (shortUrl == null) return res.sendStatus(404);

  shortUrl.clicks++;
  await shortUrl.save();

  res.redirect(shortUrl.full);
});

app.listen(3000, () => {
  console.log("服务器运行在http://localhost:3000");
});

关键修改说明

  • 登录跳转修复:把原代码中登录成功跳转的/index.ejs改为/index,路由对应控制器而非直接访问模板文件
  • 添加登录保护中间件:新增checkAuthenticated函数,确保未登录用户无法访问首页和短链接创建接口
  • 根路由重定向:设置/路由直接跳转到登录页,满足启动后先加载登录的需求
  • 修复短链接计数:原代码点击短链接未统计访问量,添加shortUrl.clicks++并执行保存操作
  • 优化依赖顺序:调整mongoose连接和模块引入位置,避免加载顺序导致的错误
  • 移除重复配置:删除重复的app.use(express.urlencoded({ extended: false }))
  • 新增登出路由:补充登出功能,完善完整的认证流程

配套模板与模型文件

  1. login.ejs(登录页)
<!DOCTYPE html>
<html>
<head>
  <title>登录</title>
</head>
<body>
  <h1>登录</h1>
  <% if (messages.error) { %>
    <p style="color: red;"><%= messages.error %></p>
  <% } %>
  <form method="POST" action="/login">
    <div>
      <label>邮箱:</label>
      <input type="email" name="email" required>
    </div>
    <div>
      <label>密码:</label>
      <input type="password" name="password" required>
    </div>
    <button type="submit">登录</button>
  </form>
  <p>还没有账号?<a href="/register">注册</a></p>
</body>
</html>
  1. register.ejs(注册页)
<!DOCTYPE html>
<html>
<head>
  <title>注册</title>
</head>
<body>
  <h1>注册</h1>
  <form method="POST" action="/register">
    <div>
      <label>姓名:</label>
      <input type="text" name="name" required>
    </div>
    <div>
      <label>邮箱:</label>
      <input type="email" name="email" required>
    </div>
    <div>
      <label>密码:</label>
      <input type="password" name="password" required>
    </div>
    <button type="submit">注册</button>
  </form>
  <p>已有账号?<a href="/login">登录</a></p>
</body>
</html>
  1. index.ejs(短链接功能页)
<!DOCTYPE html>
<html>
<head>
  <title>URL短链接工具</title>
</head>
<body>
  <h1>欢迎, <%= user.name %>!</h1>
  <form method="POST" action="/shortUrls">
    <div>
      <label>输入长链接:</label>
      <input type="url" name="fullUrl" placeholder="https://example.com" required>
      <button type="submit">生成短链接</button>
    </div>
  </form>

  <h2>已生成的短链接</h2>
  <table>
    <thead>
      <tr>
        <th>长链接</th>
        <th>短链接</th>
        <th>访问次数</th>
      </tr>
    </thead>
    <tbody>
      <% shortUrls.forEach(url => { %>
        <tr>
          <td><a href="<%= url.full %>"><%= url.full %></a></td>
          <td><a href="<%= url.short %>"><%= req.protocol + '://' + req.get('host') + '/' + url.short %></a></td>
          <td><%= url.clicks %></td>
        </tr>
      <% }) %>
    </tbody>
  </table>

  <form method="POST" action="/logout">
    <button type="submit">登出</button>
  </form>
</body>
</html>
  1. models/shortUrl.js(MongoDB模型)
const mongoose = require('mongoose');
const shortId = require('shortid');

const shortUrlSchema = new mongoose.Schema({
  full: {
    type: String,
    required: true
  },
  short: {
    type: String,
    required: true,
    default: shortId.generate
  },
  clicks: {
    type: Number,
    required: true,
    default: 0
  }
});

module.exports = mongoose.model('ShortUrl', shortUrlSchema);

依赖安装

执行以下命令安装所有所需依赖包:

npm install express ejs bcrypt passport passport-local express-flash express-session mongoose shortid dotenv

内容的提问来源于stack exchange,提问作者moonstar

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.12 14:24:51