⚡后端安全基石:JWT 原理与身份验证实战

大家好,我是大布布将军。

在你的 BFF 服务中,你不可能让所有人都能调用 /orders/list 接口。我们需要一个机制来验证用户的身份和权限,这个机制在现代 Web 应用中,几乎都基于 JWT (JSON Web Token)

本篇,我们将深入 JWT 的原理,并学习如何在 Node.js 中实现一个安全的注册和登录流程。

1. 什么是 JWT?------无状态的身份证明

JWT 是一种开放标准(RFC 7519),它定义了一种紧凑且自包含的方式,用于在双方之间安全地传输信息。

核心关键词:无状态 (Stateless)。

传统的 Session/Cookie 模式需要在服务器端存储用户的会话信息(有状态)。而 JWT 的所有信息都编码在 Token 中,服务器无需存储 Session,极大提高了 Node.js 服务的可扩展性集群化能力

一个 JWT 通常由三部分组成,用点号(.)分隔:

Header.Payload.Signature\text{Header} . \text{Payload} . \text{Signature}Header.Payload.Signature

部分 内容 作用
Header (头部) 包含 Token 类型(JWT)和所使用的加密算法(如 HMAC SHA256)。 告诉接收方如何解析和验证 Token。
Payload (载荷) 存放实际的声明信息(Claims),如用户 ID (userId)、角色 (role)、Token 过期时间 (exp)。 存放用户的身份信息。
Signature (签名) 使用 Header 和 Payload 结合一个只有服务器知道的密钥 (Secret Key) 进行加密计算得出的字符串。 防止数据被篡改。 如果有人修改了 Payload,签名就会失效。

2. 实践项目:注册、登录与 Token 校验

我们使用 Node.js 中常用的 jsonwebtoken 库来实现这套流程。

步骤一:用户注册与密码安全

在用户注册时,我们绝对不能 明文存储密码。我们需要使用 哈希算法 (如 bcrypt)对密码进行单向加密。

TypeScript

复制代码
// 📁 src/auth/auth.service.ts

import * as bcrypt from 'bcrypt';
import { Injectable } from '@nestjs/common';
import { UserService } from '../user/user.service'; // 假设我们有用户服务操作数据库

@Injectable()
export class AuthService {
    
    // 密码加密存储
    async register(username: string, password_plain: string): Promise<User> {
        // 1. 生成盐值 (Salt)
        const salt = await bcrypt.genSalt(); 
        // 2. 将密码和盐值进行哈希
        const hashedPassword = await bcrypt.hash(password_plain, salt); 

        // 3. 将用户名和哈希后的密码存储到数据库
        return this.userService.createUser(username, hashedPassword);
    }
}
步骤二:用户登录与 JWT 生成

用户登录成功后,我们需要为他生成一个 JWT Token,并返回给前端。

TypeScript

复制代码
// 📁 src/auth/auth.service.ts (续)

import * as jwt from 'jsonwebtoken';
// 假设你的密钥存储在环境变量中
const JWT_SECRET = process.env.JWT_SECRET || 'YOUR_UNSAFE_SECRET_KEY'; 
const JWT_EXPIRES_IN = '1d'; // Token 1天后过期

async login(username: string, password_plain: string): Promise<string> {
    const user = await this.userService.findUserByUsername(username);
    if (!user) throw new Error('用户不存在');

    // 1. 验证密码:将用户输入的明文密码与数据库中的哈希值进行比对
    const isMatch = await bcrypt.compare(password_plain, user.hashedPassword);
    if (!isMatch) throw new Error('密码错误');

    // 2. 构造 Payload (载荷)
    const payload = { 
        userId: user.id, 
        role: user.role 
    }; 
    
    // 3. 生成 JWT Token 并签名
    const token = jwt.sign(payload, JWT_SECRET, { expiresIn: JWT_EXPIRES_IN });

    // 4. 返回 Token(前端会将其存储在 LocalStorage 或 Cookie 中)
    return token; 
}
步骤三:身份验证中间件/守卫(Token 校验)

在 BFF 层,我们需要在每一个受保护的 API 接口前,校验前端请求头中携带的 Token 是否有效。

TypeScript

复制代码
// 📁 src/middleware/auth.middleware.ts (NestJS Guard 或 Express Middleware)

import { Injectable, CanActivate, ExecutionContext, UnauthorizedException } from '@nestjs/common';
// ... 其他依赖

@Injectable()
export class AuthGuard implements CanActivate {
    canActivate(context: ExecutionContext): boolean {
        const request = context.switchToHttp().getRequest();
        const authHeader = request.headers.authorization; 

        if (!authHeader || !authHeader.startsWith('Bearer ')) {
            throw new UnauthorizedException('请求未携带 Token 或格式错误');
        }
        
        const token = authHeader.substring(7); // 提取 Token(去掉 "Bearer ")

        try {
            // 1. 验证签名和过期时间
            const payload = jwt.verify(token, JWT_SECRET) as { userId: number, role: string };
            
            // 2. 将用户信息附加到请求对象上,供 Controller/Service 使用
            request.user = payload; 
            
            return true; // 验证通过,允许访问 Controller
        } catch (error) {
            // 签名错误或 Token 过期
            throw new UnauthorizedException('Token 无效或已过期');
        }
    }
}

3. 安全性考量:JWT 的缺点

虽然 JWT 很强大,但它并非完美:

  • 无法主动失效: 一旦 Token 被签发,除非过期,否则服务器无法强制使其失效(除非引入 Redis 黑名单机制)。
  • Payload 不加密: Payload 只是被 Base64 编码,任何人都可以解析 。因此,Payload 中绝不能存储敏感信息,如密码或密钥。

总结

掌握 JWT 是成为一名合格全栈工程师的必备技能。它让你能以可扩展、无状态 的方式管理用户身份。通过 bcrypt 保证密码安全,通过 jsonwebtoken 签发和校验 Token,你的 BFF 服务就有了坚固的安全门。

下一篇,我们将进入工程化领域,学习 Docker 容器化。这是将我们写好的 Node.js 服务部署到生产环境的通行证。

相关推荐
想用offer打牌6 小时前
MCP (Model Context Protocol) 技术理解 - 第二篇
后端·aigc·mcp
崔庆才丨静觅6 小时前
hCaptcha 验证码图像识别 API 对接教程
前端
passerby60617 小时前
完成前端时间处理的另一块版图
前端·github·web components
Hello.Reader7 小时前
Flink ZooKeeper HA 实战原理、必配项、Kerberos、安全与稳定性调优
安全·zookeeper·flink
掘了7 小时前
「2025 年终总结」在所有失去的人中,我最怀念我自己
前端·后端·年终总结
崔庆才丨静觅7 小时前
实用免费的 Short URL 短链接 API 对接说明
前端
执笔论英雄7 小时前
【大模型学习cuda】入们第一个例子-向量和
学习
wdfk_prog8 小时前
[Linux]学习笔记系列 -- [drivers][input]input
linux·笔记·学习
崔庆才丨静觅8 小时前
5分钟快速搭建 AI 平台并用它赚钱!
前端