令牌(用于测试)我使其持续时间只有60秒,但它似乎永远不会过期.将它传递给Postman上的承载者,getInfo路由继续给我一个响应,就好像令牌在60秒后仍然有效.我能做错什么?

我有这个文件夹树:

├── app.js
├── node_modules
├── package-lock.json
├── package.json
├── private
│   ├── private.key
│   └── public.key
├── public
│   ├── html
│   │   └── cover.html
│   └── images
│       ├── logo-w.png
│       └── logo.png
├── request.http
├── src
│   ├── auth
│   │   ├── authService.js
│   │   └── tokenManager.js
│   ├── middleware
│   │   └── authMiddleware.js
│   └── routes
│       ├── Auth.js
│       └── info.js

我有不同的文件,但主文件是app.js

const express = require("express");
const path = require("path");
const port = process.env.PORT || 3000;
const app = express();
const fs = require('fs');
const jwt = require('jsonwebtoken');

require('dotenv').config();

app.use(express.static("public"));

app.use(express.json());

app.get("/", (req, res) => {
  res.sendFile(path.join(__dirname, "public/html", "cover.html"));
});

const infoRoute = require("./src/routes/info");
const authRoute = require("./src/routes/auth");

app.use("/info", infoRoute);
app.use("/auth", authRoute);

app.listen(port, () => {
  console.log("Server Is Running On Port", port);
});

在我的authService.js中,我模拟了一个用于测试的简单登录:

const { issueJWT, issueRefreshToken } = require('./tokenManager');
const users = [{ id: 1, username: 'test', password: 'password' }];

const login = (username, password) => {
  const user = users.find(u => u.username === username && u.password === password);
  if (!user) throw new Error('Credentials not valid');

  const tokenObject = issueJWT(user);
  const refreshToken = issueRefreshToken(user);
  return {
    ...tokenObject,
    refreshToken: refreshToken
  };
};

module.exports = { login };

在我的tokenManager.js中

const jwt = require('jsonwebtoken');
const fs = require('fs');
const path = require('path');

const PRIV_KEY = fs.readFileSync(path.join(__dirname, '../../private/private.key'), 'utf8');

const issueJWT = (user) => {
  const expiresIn = 60;
  const payload = {
    sub: user.id,
    iat: Date.now(),
  };
  const signedToken = jwt.sign(payload, PRIV_KEY, { expiresIn: expiresIn, algorithm: 'RS256' });
  return {
    token: "Bearer " + signedToken,
    expires: expiresIn
  };
};

const issueRefreshToken = (user) => {
  const expiresIn = '7d';
  const payload = {
    sub: user.id,
    iat: Date.now(),
  };
  const refreshToken = jwt.sign(payload, PRIV_KEY, { expiresIn: expiresIn, algorithm: 'RS256' });
  return refreshToken;
};

module.exports = { issueJWT, issueRefreshToken };

在我的authMiddleware.js中


const jwt = require('jsonwebtoken');
const fs = require('fs');
const path = require('path');

const PUB_KEY = fs.readFileSync(path.join(__dirname, '../../private/public.key'), 'utf8');

const authenticateJWT = (req, res, next) => {
  const authHeader = req.headers.authorization;
  if (authHeader) {
    const token = authHeader.split(' ')[1];

    jwt.verify(token, PUB_KEY, { algorithms: ['RS256'] }, (err, user) => {
      if (err) return res.sendStatus(403);
      req.user = user;
      next();
    });
  } else {
    res.sendStatus(401);
  }
};

module.exports = { authenticateJWT };

Auth.js

const express = require('express');
const router = express.Router();
const { login } = require('../auth/authService');
const jwt = require('jsonwebtoken');
const { issueJWT } = require('../auth/tokenManager');
const fs = require('fs');
const path = require('path');

const PUB_KEY = fs.readFileSync(path.join(__dirname, '../../private/public.key'), 'utf8');

// Endpoint per il login
router.post('/login', (req, res) => {
  const { username, password } = req.body;

  try {
    const { token, expires, refreshToken } = login(username, password); // Aggiunto refreshToken
    res.json({
      success: true,
      token: token,
      expiresIn: expires,
      refreshToken: refreshToken // Inviato il refreshToken nella risposta
    });
  } catch (error) {
    res.status(401).json({ success: false, message: error.message });
  }
});

// Endpoint per il rinnovo del token
router.post('/refreshToken', (req, res) => {
  const { refreshToken } = req.body;

  if (!refreshToken) {
    return res.status(401).json({ message: "Refresh Token richiesto" });
  }

  try {
    const payload = jwt.verify(refreshToken, PUB_KEY, { algorithms: ['RS256'] });

    // Aggiungi qui la logica per verificare che il refresh token sia ancora valido, per esempio controllando nel tuo database

    const user = { id: payload.sub }; // Assumi che tu possa ottenere l'utente in base al sub nel payload
    const newTokenObject = issueJWT(user);
    res.json({
      success: true,
      token: newTokenObject.token,
      expiresIn: newTokenObject.expires
    });

  } catch (error) {
    return res.status(403).json({ message: "Refresh Token non valido" });
  }
});

module.exports = router;

最后在我的info.js(保护区)

const express = require('express');
const router = express.Router();
const { authenticateJWT } = require('../middleware/authMiddleware');

router.get('/getInfo', authenticateJWT, (req, res) => {
  res.json({ message: 'Protected area' });
});

module.exports = router;

推荐答案

您需要在代码中更改三个部分.

#1 IssueJWT()

./src/auth/tokenManager.js年内

const issueJWT = (user) => {
    const expiresIn = 60; // 60 seconds
    const payload = {
        sub: user.id,
        iat: Date.now(),
        exp: Math.floor(Date.now() / 1000) + expiresIn // exp is in seconds since Unix epoch
    };
    const signed至ken = jwt.sign(payload, PRIV_KEY, { algorithm: 'RS256' });
    return {
        token: signed至ken,
        expires: expiresIn
    };
};

《持有者》并不是一本书

    token: "Bearer " + signed至ken,

    token: signed至ken,

payload.exp需要从现在开始相加(),用60秒

#2身份验证JWT()

./src/middleware/authMiddleware.js年内

const authenticateJWT = (req, res, next) => {
    const authHeader = req.headers.authorization;
    if (authHeader) {
        const token = authHeader.split(' ')[1];

        const decoded至ken = jwt.decode(token, { complete: true });
        const nowInSeconds = Math.floor(Date.now() / 1000); // Convert current time to seconds
        if (decoded至ken.payload.exp < nowInSeconds) {
            // 至ken has expired
            return res.sendStatus(401);
        }

        jwt.verify(token, PUB_KEY, { algorithms: ['RS256'] }, (err, user) => {
            if (err) return res.sendStatus(403);
            req.user = user;
            next();
        });
    } else {
        res.sendStatus(401);
    }
};

The 'const decoded至ken = jwt.decode(token, { complete: true });' can extract the 'decoded至ken.payload.exp' expires mili-seconds. So you have to divide 1000 to convert seconds and compare.

Test by Postman

Get 至ken URL

POST http://localhost:3000/auth/login

Tests个选项卡中

var jsonData = JSON.parse(responseBody);
postman.setEnvironmentVariable("test-token", jsonData.token);
console.log(pm.environment.get("test-token"));

enter image description here

在身体上

{
    "username": "test",
    "password": "password"
}

enter image description here

获取用户信息

GET http://localhost:3000/info/getInfo

Within 60 seconds, call API enter image description here

超过60秒后,调用API

enter image description here


更新

how to support logout

Logout Request:当用户想要注销系统时,他们向服务器发送注销请求.该请求通常发送到特定的端点,例如/logout.

至ken Invalidation: Upon receiving the logout request, the server invalidates the user's authentication token. This means that the token will no longer be accepted for making authenticated requests.

至ken Blacklisting: 至 achieve token invalidation, the server maintains a list of invalidated tokens. When a user logs out, their token is added to this list, effectively blacklisting it.

并使共享变量令牌数组跨越/logout个(项目根)和中间件.这允许注销代码和身份验证中间件访问和操作该列表

tokenManager.js年内

let invalidated至kens = [];
module.exports = {
    add至Invalidated至kens: (token) => {
        invalidated至kens.push(token);
    },

    is至kenInvalid: (token) => {
        return invalidated至kens.includes(token);
    }
};

auth.js年内

const { add至Invalidated至kens } = require('../auth/tokenManager');
router.post('/logout', (req, res) => {
  const token = req.headers.authorization.split(' ')[1];
  add至Invalidated至kens(token);
  res.sendStatus(200); // Respond with success
});

authMiddleware.js年内

const { is至kenInvalid } = require('../auth/tokenManager');
const authenticateJWT = (req, res, next) => {
    const authHeader = req.headers.authorization;
    if (authHeader) {
        const token = authHeader.split(' ')[1];

        // Check if the token is in the invalidated tokens list
        if (is至kenInvalid(token)) {
            return res.sendStatus(401); // 至ken is invalid
        }
 ...

需要使用完整代码更新三个文件

./src/routes/auth.js年内

const express = require('express');
const router = express.Router();
const { login } = require('../auth/authService');
const jwt = require('jsonwebtoken');
const { issueJWT, add至Invalidated至kens } = require('../auth/tokenManager');

const fs = require('fs');
const path = require('path');

const PUB_KEY = fs.readFileSync(path.join(__dirname, '../../private/public.key'), 'utf8');

// Endpoint per il login
router.post('/login', (req, res) => {
  const { username, password } = req.body;

  try {
    const { token, expires, refresh至ken } = login(username, password); // Aggiunto refresh至ken
    res.json({
      success: true,
      token: token,
      expiresIn: expires,
      refresh至ken: refresh至ken // Inviato il refresh至ken nella risposta
    });
  } catch (error) {
    res.status(401).json({ success: false, message: error.message });
  }
});


// Your logout endpoint
router.post('/logout', (req, res) => {
  const token = req.headers.authorization.split(' ')[1];
  add至Invalidated至kens(token);
  res.sendStatus(200); // Respond with success
});

// Endpoint per il rinnovo del token
router.post('/refresh至ken', (req, res) => {
  const { refresh至ken } = req.body;

  if (!refresh至ken) {
    return res.status(401).json({ message: "Refresh 至ken richiesto" });
  }

  try {
    const payload = jwt.verify(refresh至ken, PUB_KEY, { algorithms: ['RS256'] });

    // Aggiungi qui la logica per verificare che il refresh token sia ancora valido, per esempio controllando nel tuo database

    const user = { id: payload.sub }; // Assumi che tu possa ottenere l'utente in base al sub nel payload
    const new至kenObject = issueJWT(user);
    res.json({
      success: true,
      token: new至kenObject.token,
      expiresIn: new至kenObject.expires
    });

  } catch (error) {
    return res.status(403).json({ message: "Refresh 至ken non valido" });
  }
});

module.exports = router;

./src/auth/tokenManager.js年内

const jwt = require('jsonwebtoken');
const fs = require('fs');
const path = require('path');

let invalidated至kens = [];

const PRIV_KEY = fs.readFileSync(path.join(__dirname, '../../private/private.key'), 'utf8');

const issueJWT = (user) => {
    const expiresIn = 60; // 60 seconds
    const payload = {
        sub: user.id,
        iat: Date.now(),
        exp: Math.floor(Date.now() / 1000) + expiresIn // exp is in seconds since Unix epoch
    };
    const signed至ken = jwt.sign(payload, PRIV_KEY, { algorithm: 'RS256' });
    return {
        token: signed至ken,
        expires: expiresIn
    };
};

const issueRefresh至ken = (user) => {
    const expiresIn = '7d';
    const payload = {
        sub: user.id,
        iat: Date.now(),
    };
    const refresh至ken = jwt.sign(payload, PRIV_KEY, { expiresIn: expiresIn, algorithm: 'RS256' });
    return refresh至ken;
};

module.exports = {
    issueJWT,
    issueRefresh至ken,
    add至Invalidated至kens: (token) => {
        invalidated至kens.push(token);
    },

    is至kenInvalid: (token) => {
        return invalidated至kens.includes(token);
    }
};

./src/middleware/authMiddleware.js年内

const jwt = require('jsonwebtoken');
const fs = require('fs');
const path = require('path');
const { is至kenInvalid } = require('../auth/tokenManager');

const PUB_KEY = fs.readFileSync(path.join(__dirname, '../../private/public.key'), 'utf8');
const authenticateJWT = (req, res, next) => {
    const authHeader = req.headers.authorization;
    if (authHeader) {
        const token = authHeader.split(' ')[1];

        // Check if the token is in the invalidated tokens list
        if (is至kenInvalid(token)) {
            return res.sendStatus(401); // 至ken is invalid
        }

        const decoded至ken = jwt.decode(token, { complete: true });
        const nowInSeconds = Math.floor(Date.now() / 1000); // Convert current time to seconds
        if (decoded至ken.payload.exp < nowInSeconds) {
            // 至ken has expired
            return res.sendStatus(401);
        }

        jwt.verify(token, PUB_KEY, { algorithms: ['RS256'] }, (err, user) => {
            if (err) return res.sendStatus(403);
            req.user = user;
            next();
        });
    } else {
        res.sendStatus(401);
    }
};

module.exports = {
    authenticateJWT,
};

How to test logout by Postman

POST http://localhost:3000/auth/logout

enter image description here

即使在60秒内调用API,也会在注销后未经授权

enter image description here

Javascript相关问答推荐

如何提取Cypress中文本

使用print This时, map 容器已在LeafletJS中初始化

在分区内迭代分区

将2D数组转换为图形

如何粗体匹配的字母时输入搜索框使用javascript?

当运行d3示例代码时,没有显示任何内容

在服务器上放置了Create Reaction App Build之后的空白页面

Regex结果包含额外的match/group,只带一个返回

在HTML语言中调用外部JavaScript文件中的函数

如何在不影响隐式类型的情况下将类型分配给对象?

使用NextJS+MongoDB+Prisma ORM获取无效请求正文,无法发布错误

在VS代码上一次设置多个变量格式

使用Document.Evaluate() Select 一个包含撇号的HTML元素

ngOnChanges仅在第二次调用时才触发

与在编剧中具有动态价值的定位器交互

使用props 将VUE 3组件导入JS文件

如何将值从后端传递到前端

递归地将JSON对象的内容上移一级

CSS网格使页面自动滚动

为什么我的InDesign Java脚本不能完全工作?