在 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]earers"), "");  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]的详细内容,更多请关注创想鸟其它相关文章!

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

(0)
打赏 微信扫一扫 微信扫一扫 支付宝扫一扫 支付宝扫一扫
表单事件绑定在 KnockoutJs 中如何工作
上一篇 2025年12月19日 20:56:56
JavaScript 中的“this”关键字到底是什么?
下一篇 2025年12月19日 20:57:21

相关推荐

发表回复

登录后才能评论
关注微信