如何使用 express 在用户浏览器的响应标头 cookie 中查找 JSON Web 令牌 (JWT) 来制作 node.js?

Posted

技术标签:

【中文标题】如何使用 express 在用户浏览器的响应标头 cookie 中查找 JSON Web 令牌 (JWT) 来制作 node.js?【英文标题】:How do I make node.js using express find a JSON Web Token (JWT) inside of a response header cookie in user's browser? 【发布时间】:2021-11-19 09:01:09 【问题描述】:

我有一个关于如何在用户的浏览器 cookie 中搜索我的 JWT 令牌的问题。

下面我有一些代码在响应标头中搜索用户浏览器中的 cookie,但我不确定如何使代码更具体并在 cookie 中搜索 JWT 令牌并验证它是否是实际的 JWT 令牌这是分配给该用户的。

const jwt = require('jsonwebtoken');
const router = require('express')();
const cookieParser = require('cookie-parser');
router.use(cookieParser());
module.exports = function(req,res,next)
        const token = req.header('Cookie');
        if (!token) 
            return res.status(403).send('Access Denied');
        
        try
            const verified = req.header('Cookie');
            req.user = verified;
            // const verified = jwt.verify(token, process.env.TOKEN_SECRET);
            // req.user = verified;
             next();
         catch (err) 
            res.clearHeader;
            res.status(403).send('Invalid Token');
        
    ;

【问题讨论】:

【参考方案1】:

希望我没有误解你的问题并浪费了很多时间。

简答:如何检索信息

使用req.bodyreq.headers。如果某些内容将包含令牌或身份验证详细信息,那么它就是这两者之一。

完整的身份验证演练:

要获取 JSON Web 令牌,您首先必须生成它们。不过,不建议实施您自己的令牌身份验证。我将在这里逐步展示如何创建一个完整的身份验证系统。

为简单起见,假设我们在文件auth.js 中有一个导出的route,这个路由将是一个子路由domain.com/auth,一个包含所有活动refreshTokensjwt 的数组:

const express = require("express")
const jwt = require("jsonwebtoken")
let route = (exports.route = express())
let refreshTokens = []

我们要做的是生成一个持久的刷新令牌,用户可以使用它来生成一个较小的 15 分钟访问令牌。之后,您使用刷新令牌等生成新的访问令牌。但要获得刷新令牌,您需要登录或注册。用户还可以注销并杀死刷新令牌。

route.post("/token", async (req, res) => 
    // Input: Refresh Token
    // Output: Access Token Generation
)

route.post("/login", async (req, res) => 
    // Input: User, Password
    // Output: Refresh Token
)

route.delete("/logout", async (req, res) => 
    // Input: Token to Remove
)

让我们从结尾开始。你有一个刷新令牌,你不会销毁它。只需根据此令牌过滤数组并提交状态。令牌从数组中清除后变得无法使用,这就是这里的目标。

route.delete("/logout", async (req, res) => 
    refreshTokens = refreshTokens.filter((token) => token != req.body.token)
    res.sendStatus(204)
)

到目前为止和我在一起?现在让我们回到开始。如果您使用电子邮件和密码登录,如果错误则回复错误消息,如果正确则接收令牌。

route.post("/login", async (req, res) => 
    const username = req.body.username
    const password = req.body.password

    // This is just a quick demonstration,
    // you would have to use the bcrypt hash
    // or other hash/salt methods. DO NOT
    // STORE passwords plaintext

    // Not existent user = Unauthorized
    if (username != 'admin') return res.sendStatus(401)
    // Wrong Password = Forbidden
    if (password != 'abc123') return res.sendStatus(403)

    const user = 
        id: 0,
        username: username,
        password: password
    

    const accessToken = generateAccessToken(user)
    const refreshToken = generateRefreshToken(user)

    let result = 
        success: true,
        accessToken: accessToken,
        refreshToken: refreshToken,
    

    res.send(result)
)

现在我们如何签署 JSON 网络令牌?我们来看看这里使用的两种方法:

function generateAccessToken(content) 
    return jwt.sign(content, process.env.ACCESS_TOKEN_SECRET, 
        expiresIn: "15m",
    )


function generateRefreshToken(content) 
    const token = jwt.sign(content, process.env.REFRESH_TOKEN_SECRET)
    refreshTokens.push(token)
    return token

两者都使用某种环境标记,但为什么呢?这就是您必须为后端生成一次的令牌。它将用作公钥。我们只需生成 15 分钟的访问令牌并将刷新令牌推送到数组。

route.post("/token", async (req, res) => 
    const refreshToken = req.body.token

    if (refreshToken == null) return res.sendStatus(401)
    if (!refreshTokens.includes(refreshToken)) return res.sendStatus(403)

    jwt.verify(refreshToken, process.env.REFRESH_TOKEN_SECRET, (err, user) => 
        if (err) return res.sendStatus(403)
        res.json( accessToken:
            generateAccessToken(
                id: 0,
                username: user.name,
                password: user.password
            )
        )
    )
)

我们验证刷新令牌,如果存在且有效,则返回一个新的访问令牌 15 分钟。令牌部分就是这样,您可以登录(创建刷新令牌)、检索访问令牌和注销(杀死刷新令牌)

使用方法:认证授权

无论您是以访客身份还是实际用户登录,管理页面都应该返回 403,而论坛板块应该有所不同。第一个是认证,第二个是授权。

让我们为每个函数创建两个函数。 Express 使用 next() 功能非常方便

exports.authenticate = function (req, res, next) 
    const authHeader = req.headers["authorization"]
    const token = authHeader?.split(" ")[1]

    jwt.verify(token || "", process.env.ACCESS_TOKEN_SECRET, (err, user) => 
        req.user = err ?  : user
        next()
    );
;

exports.authorize = function (req, res, next) 
    const authHeader = req.headers["authorization"]
    const token = authHeader?.split(" ")[1]

    if (token == null)
        return res.sendStatus(401)

    jwt.verify(token, process.env.ACCESS_TOKEN_SECRET, (err, user) => 
        if (err) return res.sendStatus(403)
        req.user = user
        next()
    )

现在您已经完成了整个身份验证系统(除了一些清理工作),可能还有注册系统。让我们利用它。

客户端你可以像这样创建一个 REST api:

POST http://localhost:8081/auth/login
Content-Type: application/json


    "username": "admin",
    "password": "abc123"


# Returns refresh and access token.

###

DELETE http://localhost:8081/auth/logout
Content-Type: application/json


    "token": "REFRESH_TOKEN"


# Logs out a user.

###

POST http://localhost:8081/auth/token
Content-Type: application/json


    "token": "REFRESH_TOKEN"



#
# This is how you can provide the access token
# when making a request to say a forum api
#

GET http://localhost:8081/forum/api/board/0
Authorization: Bearer ACCESS_TOKEN

用法:

route.get("forum/board/:id", authenticate, async (req, res) => 
    res.send(req.user)
)

localhost:8081/forum/board/7认证时的预期输出:

id:0,username:"admin",password:"abc123"

否则:


尽管如此,请勿尝试实施您自己的身份验证。真的,你不应该。

来源

https://www.youtube.com/watch?v=mbsmsi7l3r4

【讨论】:

以上是关于如何使用 express 在用户浏览器的响应标头 cookie 中查找 JSON Web 令牌 (JWT) 来制作 node.js?的主要内容,如果未能解决你的问题,请参考以下文章

在 Express 中设置默认响应标头

如何将授权标头添加到用户请求并使用 express 将其代理到另一个服务器 api

在响应标头中使用 apollo,cookie 但未设置

可以使用 launchSettings.json 启用 IIS Express 的 access-control-allow-origin 响应标头吗?

使用 Express 和 Node,如何跨子域/主机头维护会话

如何从标头中正确获取 jwt 令牌并将其与 express 一起使用