首页 > web前端 > js教程 > 正文

在 Nodejs 中进行身份验证的正确方法 [uide]

花韻仙語
发布: 2024-11-26 08:32:14
转载
571人浏览过

在 nodejs 中进行身份验证的正确方法 [uide]

身份验证是后端开发中最关键但经常被误解的方面之一。由于其复杂性,开发人员经常转向第三方解决方案,例如 auth0 或 supabase。虽然这些都是优秀的工具,但构建您自己的身份验证系统可以提供更大的灵活性和控制力。

在本指南中,您将了解如何以最少的依赖关系为 express.js api 服务实现简单的身份验证中间件。到最后,您将拥有:

  • 功能齐全的用户名 密码身份验证。
  • 与 postgresql 集成来存储用户帐户。
  • 基于 jwt 的身份验证中间件。
  • 通过自动重用检测刷新令牌以增强安全性。

本指南注重简单性,避免使用诸如 passport.js 之类的包来降低复杂性。


设置用户帐户表

首先,创建一个 postgresql 表来存储用户帐户:

create table users (
    "id" serial primary key,
    "username" varchar(255) unique not null,
    "password" varchar(255) not null,
    "email" varchar(255) unique,
    "created_at" timestamp not null default now()
);
登录后复制

jwt 身份验证中间件

接下来,创建 jwt 身份验证中间件来保护 api 端点。此示例使用对称加密。对于微服务架构,请考虑使用带有公钥/私钥对的非对称加密。

中间件代码(/src/middleware/jwt.ts):

import jwt from "jsonwebtoken";

const jwt_secret_key = process.env.jwt_secret_key as string; // randomly generated. min length: 64 characters

export const protectedroute: requesthandler = async (req, _, next) => {
  const authheader = req.header("authorization");

  if (!authheader) {
    return next(notauthenticated());
  }

  const accesstoken = authheader.replace(new regexp("\b[bb]earer\s"), "");

  try {
    const { userid } = validatejwt(accesstoken);
    const user = await userrepository.getuserbyid(parseint(userid));

    if (user) {
      req.user = user;
      next();
    } else {
      next(invalidaccesstoken());
    }
  } catch (err) {
    next(invalidaccesstoken());
  }
};

const validatejwt = (token: string, verifyoptions?: jwt.verifyoptions) => {
  const jwtverifyoptions = object.assign(
    { algorithms: "hs256" },
    verifyoptions,
    {
      issuer: "yourapi.com",
      audience: "yourapi.com:client",
    }
  );
  return jwt.verify(token, jwt_secret_key, jwtverifyoptions) as t;
};
登录后复制

使用中间件来保护路由:

import { protectedroute } from "@/middleware/jwt";

router.get("/user", protectedroute, async (req, res, next) => {
  const user = req.user!;
  res.json({ user });
});
登录后复制

创建身份验证控制器

现在,实现用于注册和登录的控制器:

注册控制器:

import argon from "argon2";

const signup = async (props) => {
  const { username, password, email } = props;

  await userrepo.getuser(username).then((res) => {
    if (res !== null) throw usernamenotavailable();
  });

  const hashedpass = await argon.hash(password, {
    timecost: 2,
    parallelism: 1,
    memorycost: 19456,
  });

  const newuser = await createuser({
    username,
    hashedpass,
    email,
  });

  const refreshtoken = await generaterefreshtoken(newuser.userid);
  const accesstoken = generateaccesstoken(newuser.userid);

  const { password: _, ...userres } = newuser;
  return { user: userres, accesstoken, refreshtoken };
};
登录后复制

登录控制器:

const login = async (props) => {
  const { username, password } = props;

  const user = await getuser(username).then((res) => {
    if (res === null) throw invalidlogincredentials();
    return res;
  });

  const isok = await argon.verify(user.password, password);

  if (isok) {
    const refreshtoken = await generaterefreshtoken(user.userid);
    const accesstoken = generateaccesstoken(user.userid);

    const { password: _, ...userres } = user;
    return { user: userres, accesstoken, refreshtoken };
  }

  throw invalidlogincredentials();
};
登录后复制

存储刷新令牌

刷新令牌提供长期身份验证。让我们创建一个数据库表来存储它们:

create table refresh_tokens (
    "id" serial primary key,
    "token" uuid not null default gen_random_uuid(),
    "token_family" uuid not null default gen_random_uuid(),
    "user_id" integer not null references users(id) on delete cascade,
    "active" boolean default true,
    "expires_at" timestamp not null,
    "created_at" timestamp not null default now()
);
登录后复制

代币生成器:

import jwt from "jsonwebtoken";

const jwt_secret_key = process.env.jwt_secret_key as string; // randomly generated. min length: 64 characters

const generateaccesstoken = (userid: number) => {
  const jwtsignoptions = object.assign(
    { algorithm: "hs256" },
    {},
    {
      issuer: "yourapi.com",
      audience: "yourapi.com:client",
    }
  );
  return jwt.sign({ userid: userid.tostring() }, jwt_secret_key, jwtsignoptions);
};

const generaterefreshtoken = async (userid: number, tokenfamily?: string) => {
  const expat = new date(new date().gettime() + 31 * 24 * 60 * 60 * 1000); // expire in 31 days
  const refreshtokenexp = expat.toisostring();

  const token = await createtokenquery({
    userid,
    tokenfamily,
    expiresat: refreshtokenexp,
  });

  return token;
};
登录后复制

刷新令牌逻辑:

实现逻辑来安全地处理刷新令牌:

const refreshToken = async ({ token }: RefreshTokenSchema) => {
  const tokenData = await getRefreshToken(token);

  if (!tokenData) throw forbiddenError();

  const { userId, tokenFamily, active } = tokenData;

  if (active) {
    // Token is valid and hasn't been used yet
    const newRefreshToken = await generateRefreshToken(userId, tokenFamily);
    const accessToken = generateAccessToken(userId);

    return { accessToken, refreshToken: newRefreshToken };
  } else {
    // Previously refreshed token used, invalidate all tokens in family
    await invalidateRefreshTokenFamily(tokenFamily);

    throw forbiddenError();
  }
};
登录后复制

在这篇 auth0 文章中了解有关刷新令牌和自动重用检测的更多信息。


结论

通过遵循本指南,您已经为 node.js api 构建了一个简单、安全的身份验证系统,并且依赖性最小。这种方法可确保您拥有完全控制权并遵守现代最佳安全实践。

如果您想节省时间和精力,请查看 vratix。我们的开源 cli 可以在几秒钟内建立一个功能齐全的 node.js 项目并进行身份验证。在 github 上探索我们完全实现的身份验证模块。


本指南对您有帮助吗?请在评论中告诉我们,或通过 x 与我们联系!

以上就是在 Nodejs 中进行身份验证的正确方法 [uide]的详细内容,更多请关注php中文网其它相关文章!

最佳 Windows 性能的顶级免费优化软件
最佳 Windows 性能的顶级免费优化软件

每个人都需要一台速度更快、更稳定的 PC。随着时间的推移,垃圾文件、旧注册表数据和不必要的后台进程会占用资源并降低性能。幸运的是,许多工具可以让 Windows 保持平稳运行。

下载
来源:dev.to网
本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系admin@php.cn
最新问题
开源免费商场系统广告
热门教程
更多>
最新下载
更多>
网站特效
网站源码
网站素材
前端模板
关于我们 免责申明 意见反馈 讲师合作 广告合作 最新更新
php中文网:公益在线php培训,帮助PHP学习者快速成长!
关注服务号 技术交流群
PHP中文网订阅号
每天精选资源文章推送
PHP中文网APP
随时随地碎片化学习
PHP中文网抖音号
发现有趣的

Copyright 2014-2025 https://www.php.cn/ All Rights Reserved | php.cn | 湘ICP备2023035733号