• 技术文章 >web前端 >js教程

    浅析nodejs中怎么使用JWT?

    青灯夜游青灯夜游2023-01-04 21:00:18转载91
    本篇文章带大家了解一下JWT,介绍一下JWT在node中的应用,以及JWT的优缺点,希望对大家有所帮助!

    什么是JWT

    JWT也就是JSON Web Token的缩写,也就是为了在网络应用环境中一种认证解决方案,在传统的认证机制中,无非是一下几个步骤:

    1. 用户将账号密码发送到服务器;
    
    2. 服务器通过验证账号密码后,会在当前session中保存一些用户相关的信息,用户角色或者过期时间等等;
    
    3. 服务器给用户一个session_id, 写入用户的Cookie或者客户端自行保存在本地;
    
    4. 用户每次请求服务,都需要带上这个session_id,或许会通过Cookie,或者其他的方式;
    
    5. 服务器接收到后,回去数据库查询当前的session_id,校验该用户是否有权限;

    这种模式有一种优势在于,服务器随时可以终止用户的权限,可以去数据库修改或者删除当前用户的session信息。但是也有一点不好的,就是如果是服务器集群的话,所有的机器就需要共享这些session信息,确保每台服务器都能够获取到相同的session存储信息。虽然可以解决这些问题,但是工程量巨大。

    JWT方案的优势呢,就是不保存这些信息,token数据保存在客户端,每次接受请求时,只需要校验就好。【相关教程推荐:nodejs视频教程编程教学

    JWT的原理

    简单说一下JWT的原理,其实就是客户端发送请求认证的时候,服务器在认证用户之后,会生成一个JSON对象,大概包括“你是谁,你是干嘛的等等,到期时间”这些信息,重要的是一定要有到期时间;大致格式为:

    {
        username: "贼烦字符串er",
        role: "世代码农",
        endTime: "2022年5月20日"
    }

    但是不会用这么肤浅的方式传给你,它会通过制定的签名算法和你提交的payload的一些信息进行可逆的签名算法进行签名后传输,大致的格式我用一张图片表示:

    由图片可以看出,返回的信息大致分为三部分,左侧为签名之后的结果,也就是返回给客户端的结果,右侧也是就Decoded的源码了,三部分由“点”隔开,分别由红、紫、青三种颜色一一对应:

    除了这些字段,你还可以搞一些自定义的字段,由于JWT默认是不加密的,所以在使用的时候尽量注意不要使用一些敏感数据。

    JWT的简单使用

    下面我们来感受一下具体的使用:

    第一步:我们需要搭建一个node的项目;通过npm init -y初始化一个项目;之后我们需要安装依赖,分别按状expressjsonwebtokennodemon三个依赖:

    $ npm i express jsonwebtoken nodemon

    之后在package.json中的scripts字段中添加nodemon app.js命令:

    "scripts": {
        "start": "nodemon app.js"
    },

    第二步:初始化一下node应用,在根目录下创建app.js文件;

    // app.js
    
    const express = require("express");
    const app = express();
    
    app.use(express.json());
    
    app.listen(3000, () => {
      console.log(3000 + " listening..."); // 监听3000端口
    });

    第三步:引入jsonwebtoken依赖,并且创建接口和服务器的私钥;

    // app.js
    
    //...
    const jwt = require("jsonwebtoken");
    
    const jwtKey = "~!@#$%^&*()+,";
    // ...

    这里面的jwtKey是我们自定义保存仅限保存在服务器中的私钥,之后我们开始写一个 /login 接口,用来登录,并且创建本地的模拟数据库用来校验,并通过jwt.sign方法进行校验签名:

    // app.js
    const database = {
      username: "username",
      password: "password",
    };
    
    app.post("/login", (req, res) => {
      const { username, password } = req.body;
      if (username === database.username && password === database.password) {
        jwt.sign(
          {
            username,
          },
          jwtKey,
          {
            expiresIn: "30S",
          },
          (_, token) => {
            res.json({
              username,
              message: "登陆成功",
              token,
            });
          }
        );
      }
    });

    上面代码中我们创建了database变量来模拟创建了本地的账号密码数据库,用来校验登陆;接下来建立了一个/loginpost接口,在校验账号密码完全匹配之后,我们通过jsonwebtoken包导入的jwt对象下的人sign方法进行签名,这个方法有三种接口签名:

    export function sign(
        payload: string | Buffer | object,
        secretOrPrivateKey: Secret,
        options?: SignOptions,
    ): string;
    
    export function sign(
        payload: string | Buffer | object,
        secretOrPrivateKey: Secret,
        callback: SignCallback,
    ): void;
    
    export function sign(
        payload: string | Buffer | object,
        secretOrPrivateKey: Secret,
        options: SignOptions,
        callback: SignCallback,
    ): void;

    这里用到了函数重载的方式实现接口,我们这里将实现最后一个接口签名,第一个参数可以是一个自定义的对象类型,也可以是一个Buffer类型,还可以直接是一个string类型,我们的源码使用了object类型,自定义了一些字段,因为jwt在进行签名是也会对这些数据一并进行签名,但是值得注意的是,这里尽量不要使用敏感数据,因为JWT默认是不加密的,它的核心就是签名,保证数据未被篡改,而检查签名的过程就叫做验证

    当然你也可以对原始Token进行加密后传输;

    第二个参数:是我们保存在服务器用来签名的秘钥,通常在客户端-服务端模式中,JWS 使用 JWA 提供的 HS256 算法加上一个密钥即可,这种方式严格依赖密钥,但在分布式场景,可能多个服务都需要验证JWT,若要在每个服务里面都保存密钥,那么安全性将会大打折扣,要知道,密钥一旦泄露,任何人都可以随意伪造JWT。

    第三个参数:是签名的选项SignOptions,接口的签名:

    export interface SignOptions {
        algorithm?: Algorithm | undefined;
        keyid?: string | undefined;
        expiresIn?: string | number | undefined;
        /** expressed in seconds or a string describing a time span [zeit/ms](https://github.com/zeit/ms.js).  Eg: 60, "2 days", "10h", "7d" */
        notBefore?: string | number | undefined;
        audience?: string | string[] | undefined;
        subject?: string | undefined;
        issuer?: string | undefined;
        jwtid?: string | undefined;
        mutatePayload?: boolean | undefined;
        noTimestamp?: boolean | undefined;
        header?: JwtHeader | undefined;
        encoding?: string | undefined;
    }

    这里我们用的是expiresIn字段,指定了时效时间,使用方法参考这个文档;

    第四个参数是一个回调,回调的第二个参数就是我们通过签名生成的token,最后将这个token返回给前端,以便存储到前端本地每次请求是带上到服务端进行验证。

    接下来,我们来验证一下这个接口: 我是在vscode安装的REST Client插件,之后在根目录创建一个request.http的文件,文件内写上请求的信息:

    POST http://localhost:3000/login
    content-type: application/json
    
    {
      "username": "username",
      "password": "password"
    }

    之后在命令行执行npm run start命令启动服务,之后在requset.http文件上方点击Send Request按钮,发送请求:

    请求成功后,会看到这样的响应报文:

    token字段就是我们JWT生成的token;

    下面来验证一下这个token是否有效,我们在写一个登录过后的接口:

    app.get("/afterlogin", (req, res) => {
      const { headers } = req;
      
      const token = headers["authorization"].split(" ")[1];
      // 将token放在header的authorization字段中
      jwt.verify(token, jwtKey, (err, payload) => {
        if (err) return res.sendStatus(403);
        res.json({ message: "认证成功", payload });
      });
    });

    这段代码中,通过获取请求头中的authorization字段中的token进行获取之前通过JWT生成的token。 之后通过调用jwt.verify校验方法校验这个token是否有效,这个方法分别有三个参数:

    // 有四个接口签名,可以自行查文档
    
    export function verify(
        token: string,
        // 需要检验的token
        secretOrPublicKey: Secret | GetPublicKeyOrSecret,
        // 定义在服务器的签名秘钥
        callback?: VerifyCallback<JwtPayload | string>,
        // 获取校验信息结果的回调
    ): void;

    接下来我们把刚才响应的token复制到请求头中:

    ###
    GET http://localhost:3000/afterlogin
    authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJ1c2VybmFtZSI6InVzZXJuYW1lIiwiaWF0IjoxNjUyNzg5NzA3LCJleHAiOjE2NTI3ODk3Mzd9.s9fk3YLhxTUcpUgCfIK4xQN58Hk_XEP5y9GM9A8jBbY

    前面的Bearer认证, 是http协议中的标准认证方式

    同样点击Send Request当看到下面图片的响应,就意味着响应成功:

    其实以上就是JWT的一些简单的用法,接下来再说一下JWT本身存在的优缺点.

    JWT的不足

    很明显,随着payload的信息量增大,token的长度也会增加;

    总结

    以上主要讲了几点:

    这个是上面nodejs的demo源码,可供参考;

    https://github.com/wangzi6224/jwt-usage-nodejs

    更多node相关知识,请访问:nodejs 教程

    以上就是浅析nodejs中怎么使用JWT?的详细内容,更多请关注php中文网其它相关文章!

    声明:本文转载于:掘金社区,如有侵犯,请联系admin@php.cn删除
    专题推荐:JWT node
    上一篇:10 个编写更简洁React代码的实用小技巧 下一篇:自己动手写 PHP MVC 框架(40节精讲/巨细/新人进阶必看)

    相关文章推荐

    • linux node cnpm怎么安装• pm2启动node服务失败怎么办• node安装完后npm不能用怎么办• 什么是node中间件• node http get 乱码怎么办• 一文详解Nodejs怎么卸载(步骤分享)
    1/1

    PHP中文网