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

在 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 charactersexport 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 charactersconst 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]的详细内容,更多请关注【创想鸟】其它相关文章!

版权声明:本文内容由互联网用户自发贡献,该文观点仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌抄袭侵权/违法违规的内容, 请发送邮件至253000106@qq.com举报,一经查实,本站将立刻删除。

发布者:PHP中文网,转转请注明出处:https://www.chuangxiangniao.com/p/2648422.html

(0)
上一篇 2025年3月7日 08:10:30
下一篇 2025年2月26日 11:37:41

AD推荐 黄金广告位招租... 更多推荐

相关推荐

  • 表单事件绑定在 KnockoutJs 中如何工作

    此内容基本上是原始材料的翻译。目的是了解 magento 2 的 knockoutjs 并用葡萄牙语创建有关 knockoujs 的内容。 文档 数据绑定语法绑定上下文“点击”绑定“事件”绑定“提交”绑定“启用”和“禁用”绑定“值”绑定“t…

    2025年3月7日
    200
  • Performance Optimization with Middleware in Nodejs

    中间件通过在请求-响应周期的各个阶段拦截和处理请求,在 node.js 应用程序中发挥着至关重要的作用。虽然中间件提供了灵活性和模块化,但如果不能有效实施,它也可能成为瓶颈。在这篇文章中,我们将探讨优化中间件的策略,以确保您的应用程序发挥最…

    2025年3月7日
    200
  • 使用 Jest 掌握模拟 API 调用:综合教程

    使用 jest 模拟 api 调用对于编写高效、快速且可靠的测试至关重要。本教程将指导您了解使用 jest 的广泛库和高级场景适配器来控制模拟响应的基本技术。 在为进行 api 调用的代码编写测试时,模拟这些调用非常重要。此策略可确保您的测…

    2025年3月7日 编程技术
    200
  • 什么是 TypeScript 以及为什么要使用它?

    typescript 是一个功能强大的 javascript 扩展,它因使 web 和应用程序开发更加安全、可扩展和高效而广受欢迎。我们将探讨 typescript 是什么、为什么在您的下一个项目中考虑使用它,以及它如何改善您的开发体验。 …

    2025年3月7日
    200
  • 本周 JavaScript 2

    javascript 不断发展。最新的重大更新 ecmascript 2023 (es14) 于 2023 年 6 月发布。此更新引入了多项新功能,增强了语言的功能并提高了开发人员的效率。 ecmascript 2023 的主要特性 1。顶…

    2025年3月7日
    200
  • NgSysV: Firestore CRUD templates

    该帖子系列已在 ngatesystems.com 上建立索引。您还可以在那里找到超级有用的关键字搜索工具。 最后评论:24 年 11 月 介绍 google 提供的关于 firestore crud(创建、读取、更新、删除)说明的大量在线文…

    2025年3月7日
    200
  • &#新&#关键字

    在 javascript 中,“new”关键字通过构造函数创建对象的新实例。 新关键字的目的: 对象创建。原型链接。绑定“this”并返回新创建的对象。 运作原理: 当您在构造函数中使用 new 关键字时,将执行以下步骤: 创建了一个新的空…

    2025年3月7日
    200
  • 谷歌个人资料设计

    Google 不仅是一家商业公司,而且还是一项便捷的国际服务,用于智能存储有关用户及其需求的数据。 在我看来,与 Mail 相比,这是一个规模更大的系统,涵盖了与用户合作的许多微妙之处,包括考虑到个人数据法。 当我们谈论社交网络上的个人资料…

    2025年3月7日
    200
  • 简单干净的代码#1

    更改此: if (user.active === true && user.age >= 18 && user.email !== null && user.email !== unde…

    2025年3月7日
    200
  • Astro中的神秘显示:揭开开发环境的秘密

    静态站点生成器之旅 多年来,我一直致力于 wordpress 项目,但最近我转向了 astro。借助人工智能的定制帮助,我以为我可以创建自己的主题,但那是天真的。 tailwindcss 对我来说是新的 – 我以前甚至没有使用过…

    2025年3月7日
    200

发表回复

登录后才能评论