验证时 JWT 令牌始终无效

Posted

技术标签:

【中文标题】验证时 JWT 令牌始终无效【英文标题】:JWT token is always invalid when verified 【发布时间】:2021-02-12 01:41:54 【问题描述】:

我是 node.js、express 和 JWT 的新手。我在这里发现了类似的问题,但它们没有帮助。

我能够登录并将令牌存储在本地存储中,但是当我尝试为具有相同令牌的另一个请求设置授权标头时,它在服务器中的验证失败。我从服务器和客户端检查了令牌,它们完全相同,但验证失败,请帮助!

这是我用来验证令牌的代码。

exports.verify = function(req, res, next) 
    let accessToken = req.headers.authorization
    if (!accessToken)
        return res.status(403).send()
    
    
    let payload
    try
        // Never makes it through this

        payload = jwt.verify(accessToken, process.env.ACCESS_TOKEN_SECRET)
        next()
    
    catch(e)
        
        return res.status(401).json(success: false, message: "token expired or invalid")
    

app.js 文件中,我将verify 这样的函数用于另一条路线。

const  verify  = require('./controllers/auth')

const userRoutes = require('./routes/userRoutes')

app.use('/user', verify, userRoutes)

我哪里错了?

编辑:

我在verify 函数中添加了console.log(e),在catch() 内部得到了以下结果。

TokenExpiredError: jwt 已过期 在/home/shashank/Documents/sms/server/node_modules/jsonwebtoken/verify.js:152:21 在 getSecret (/home/shashank/Documents/sms/server/node_modules/jsonwebtoken/verify.js:90:14) 在 Object.module.exports [作为验证] (/home/shashank/Documents/sms/server/node_modules/jsonwebtoken/verify.js:94:10) 在exports.verify (/home/shashank/Documents/sms/server/controllers/auth.js:62:23) 在 Layer.handle [as handle_request] (/home/shashank/Documents/sms/server/node_modules/express/lib/router/layer.js:95:5) 在 trim_prefix (/home/shashank/Documents/sms/server/node_modules/express/lib/router/index.js:317:13) 在/home/shashank/Documents/sms/server/node_modules/express/lib/router/index.js:284:7 在 Function.process_params (/home/shashank/Documents/sms/server/node_modules/express/lib/router/index.js:335:12) 在下一个(/home/shashank/Documents/sms/server/node_modules/express/lib/router/index.js:275:10) 在 cookieParser (/home/shashank/Documents/sms/server/node_modules/cookie-parser/index.js:57:14) 过期时间:2020-10-29T17:30:31.000Z

让我展示一下我存储密钥信息的 .env 文件

ACCESS_TOKEN_SECRET=swsh23hjddnns
ACCESS_TOKEN_LIFE=3600
REFRESH_TOKEN_SECRET=dhw782wujnd99ahmmakhanjkajikhiwn2n
REFRESH_TOKEN_LIFE=86400

那么,访问令牌必须持续一个小时吧?

令牌的创建如下所示

const jwt = require('jsonwebtoken')

// Not using a database right now.
let users = 
    email: 'myemail@gmail.com',
    password: 'password'


exports.login = function(req, res) 

    let email = req.body.email
    let password = req.body.password
    
    // Simple validation !
    if (!email || !password || users.email !== email || users.password !== password)
        return res.status(401).json(success: false, message: "Incorrect username or password")
        


    //use the payload to store information about the user such as username, user role, etc.
    let payload = email: email

    //create the access token with the shorter lifespan
    let accessToken = jwt.sign(payload, process.env.ACCESS_TOKEN_SECRET, 
        algorithm: "HS256",
        expiresIn: process.env.ACCESS_TOKEN_LIFE
    )

    //create the refresh token with the longer lifespan
    let refreshToken = jwt.sign(payload, process.env.REFRESH_TOKEN_SECRET, 
        algorithm: "HS256",
        expiresIn: process.env.REFRESH_TOKEN_LIFE
    )

    //store the refresh token in the user array
    users.refreshToken = refreshToken

    //send the access token to the client inside a cookie
    // res.cookie("jwt", accessToken,  httpOnly: true)   //secure: false,   use this along with httpOnly: true in production
    
    // res.setHeader('Authorization', accessToken); 
    res.json(
        accessToken: accessToken,
        success: true, message: "Authentication success"
    );
    res.send()


【问题讨论】:

你能说明你在哪里创建令牌以及如何使用环境变量 所以令牌只是过期了。要么只是超出了您设置的时间(故意),要么您错误地设置了到期时间并且到期时间比预期的要早。但是您知道,查看所有细节很重要,稍后在您的代码中,您还可以以不同的方式对不同的异常做出反应。 所以,我登录并在几秒钟后发出请求,但令牌应该持续一个小时! ,您认为这与时区有关吗?我不知道。 @LawrenceCherone 我已经为您更新了问题详情。 当你在jwt.io上解码时,你能显示实际的token还是只显示iat和exp的值? 我刚刚在响应中发送了 process.env.ACCESS_TOKEN_LIFE 的值,它是 "3600" ,也许这是问题所在?然后我在设置过期时间时尝试了parseInt(),现在我在响应中没有得到状态401,非常感谢! 【参考方案1】:

您遇到的问题似乎是因为您存储超时期限的方式。

来自node-jsonwebtoken 的文档

expiresIn:以秒或描述时间跨度的字符串表示 时间/毫秒。例如:60、“2 天”、“10 小时”、“7 天”。数值被解释 作为秒数。如果您使用字符串,请确保提供时间 单位(天、小时等),否则使用毫秒单位 默认(“120”等于“120ms”)。

因为您存储在您的 process.env 中,所以看起来它正在将其转换为字符串,而不是维护整数值。

测试代码:

const jwt = require('jsonwebtoken')
require('dotenv').config();
let payload = email: 'email'
let accessToken = jwt.sign(payload, process.env.ACCESS_TOKEN_SECRET, 
    algorithm: "HS256",
    expiresIn: process.env.ACCESS_TOKEN_LIFE
)
console.log(accessToken);
console.log('waiting 4 seconds');
setTimeout(function() 
    let val = jwt.verify(accessToken, process.env.ACCESS_TOKEN_SECRET);
    console.log(val);
, 4000);

使用以下 process.env 值,它会失败

ACCESS_TOKEN_SECRET=swsh23hjddnns
ACCESS_TOKEN_LIFE=3600
REFRESH_TOKEN_SECRET=dhw782wujnd99ahmmakhanjkajikhiwn2n
REFRESH_TOKEN_LIFE=86400

但是如果我们将 ACCESS_TOKEN_LIFE 更改为

ACCESS_TOKEN_LIFE=3600S

成功了

如果没有时间单位,任何延迟超过 3.6 秒的请求都会出错。

【讨论】:

以上是关于验证时 JWT 令牌始终无效的主要内容,如果未能解决你的问题,请参考以下文章

JWT 给 JsonWebTokenError “无效令牌”

Laravel JWT 令牌在身份验证 JWT 方法中刷新后无效

JWT 身份验证令牌在非 /api 路由中无效

验证 Azure 广告访问令牌时签名无效,但 id 令牌有效

在 ExpressJs 中使用时 JWT 中的多个问题

如何在没有守卫装饰器的情况下始终验证 JWT? (Nest.js + 护照)