jwt - 在 Vue.js 中的何处存储令牌?

jwt - Where to store token in Vue.js?

我在一些安全博客上读到,将令牌存储在本地存储中是不安全的,所以我想做的是将令牌存储在 vuex 存储中,并且所有 api 调用都将包含该令牌以下请求。

但是我在登录成功的时候无法第一时间访问到token,我想做的是第一次将token存储在vuex存储中,我想到了将token发送到body 的响应,但这将是一个易受攻击的方法,所以我在 header["authorization"].

中发送它

下面分别是我的 user.js 和 login.vue 文件。

router.post('/login', function (req, res, next) {
    const {
        UserName,
        Password
    } = req.body;

    if (UserName.length == 0 || Password.length == 0) {
        res.status(400).json({
            message: 'Email or Password is empty',
        });
    } else {
        login_pool.query(
            'SELECT * FROM authentication WHERE user_name = () and password = crypt((), password)',
            [UserName, Password],
            (err, results) => {
                if (err) {
                    throw err;
                } else if (results.rows.length == 1) {
                    // On Successful Login
                    const token = jwt.sign(
                        {
                            user_name: results.rows[0].user_name,
                            full_name: results.rows[0].full_name,
                            phone_number: results.rows[0].phone_number,
                        },
                        btoa(process.env.TOKEN_SECRET), // converting token_secret to base 64
                        { expiresIn: '1800s' },
                        { algorithm: 'HS256' },
                        (err) => {
                            if (err) {
                                res.status(400).json({
                                    message: 'Not able to create a token',
                                });
                                console.log(err);
                            }
                        }
                    );
                    res.header('Authorization', `Bearer ${token}`);
                    res.status(201).json({
                        message: results.rows[0].full_name + 'logged in.',
                    });
                    console.log(results.rows[0].full_name + 'Just Logged In. ');
                } else {
                    login_pool.query(
                        'SELECT * FROM authentication WHERE user_name = ()',
                        [UserName],
                        (errUser, resultUser) => {
                            if (resultUser.rows.length != 1) {
                                res.status(400).json({
                                    message: 'User with this email does not exist',
                                });
                            } else {
                                res.status(400).json({
                                    message: 'Password is not correct',
                                });
                            }
                        }
                    );
                }
            }
        );
    }
});
LoginSubmit() {
    this.axios
        .post(
            "http://127.0.0.1:3000/users/login",
            {
                UserName: this.UserName,
                Password: this.Password,
            },
            {
                headers: {
                    "Content-Type": "application/json;charset=UTF-8",
                    "Access-Control-Allow-Origin": "*",
                    Accept: "application/vnd.api+json",
                },
            }
        )
        .then(
            (res) => {
                // successful login
                console.log(res.headers); // authentication header not present here
                this.Error = "";
                console.log(this.axios.defaults.headers.common); // authentication header not present here
            },
            (err) => {
                console.log(err.response.data.message);
                this.Error = err.response.data.message.replace(/"/g, "");
            }
        );
},
res.header({
   Authorization: "Bearer" + token,
   "Access-Control-Expose-Headers": "Authorization",
});

使用 Access-Control-Expose-Headers 解决了我的问题,现在我可以使用 res.headers["authorization"]

在前端访问授权 Header

我从没见过这样的。 JWT 可以作为 body 响应的一部分发送到某些 POST /auth 端点。然后,您使用某种存储方式保存该令牌。

到底是localStorage还是cookie还有待商榷;我个人只是使​​用 localStorage,这样 API 就可以尽可能无状态。但是,当使用 cookie 时,您可以为您的 cookie 设置某种过期时间,以确保在过期日期后将其删除。

Vuex 商店本质上是一个全局状态 object,一旦您刷新浏览器 window,它就会丢失所有内容。那是除非你将它与某种 localStorage/sessionStorage 结合起来 loads/saves 到你的 Vuex 商店。

所以我建议您删除 Access-Control-Expose-Headers: Authorization header 并在身份验证期间将 JWT 令牌作为 POST body 响应发送。此类授权请求的安全性取决于您使用的是 http 还是 https。您显然总是想使用 https,因为(wifi-)网络的某些恶意所有者可以轻松读取所有不是 plain-text 的内容。

我们的 vue 应用程序将令牌存储在 sessionStorage 中,因此 运行 陷入一个奇怪的问题。

我们看到和预期的正常行为是每次用户在新选项卡上访问我们的应用程序时,他们都必须进行身份验证。这在所有浏览器中都能正常工作,一直。只有 Windows Chrome 上只有一条路径,用户无需进行身份验证。

  1. 确保至少有一个 Chrome 的其他选项卡或实例打开,是否加载网站无关紧要。
  2. 登录我们的网站
  3. 关闭标签。
  4. 打开 Chrome 的新选项卡或新实例。
  5. 转到浏览器历史记录并恢复刚刚关闭的选项卡。

在这种情况下,sessionStorage 已恢复并且用户已登录。我已经能够在一个简单的 HTML 页面中复制它。

除了 sessionStorage 之外,还有其他方法可以保存 JWT 令牌吗?

<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>Session Persists Test</title>
    <style>
        div { margin: .75em; }
        input { margin-left: 2em;}
    </style>
    <script src="//ajax.googleapis.com/ajax/libs/jquery/2.0.3/jquery.min.js"></script>
    <script>
        $(document).ready(function () {
            const formatDateTime = function (date_ob) {
                let date = ("0" + date_ob.getDate()).slice(-2);
                let month = ("0" + (date_ob.getMonth() + 1)).slice(-2);
                let year = date_ob.getFullYear();
                let hours = ("0" + date_ob.getHours()).slice(-2);
                let minutes = ("0" + date_ob.getMinutes()).slice(-2);
                let seconds = ("0" + date_ob.getSeconds()).slice(-2);
                return `${month}-${date}-${year} ${hours}:${minutes}:${seconds}`;
            };

            const updateTimes = () => {
                let now_token = formatDateTime(new Date());
                let session_now_token = sessionStorage.getItem('now_token');

                if (session_now_token == null) {
                    session_now_token = now_token;
                    sessionStorage.setItem('now_token', session_now_token);
                }
                $("#sessionTime").text(`Session Time: ${session_now_token}`);
                $("#lastCall").text(`Current Time: ${now_token}`);
            }

            $("#updateSession").click(function () {
                sessionStorage.clear();
                location.reload();
            });

            $('#updateTime').click(updateTimes);

            updateTimes();
        });
    </script>
</head>
<body>
<h1>Session Persists Test</h1>
<div><span id="sessionTime">Session Time: (please enable Javascript not running>)</span><input id="updateSession" type="button" value="Update Session Time"></div>
<div><span id="lastCall">Current Time: (please enable Javascript not running>)</span><input id="updateTime" type="button" value="Update Current Time (or press F5)"></div>
<H3>Testing of how long a browser session persists.</H3>
<div>
    <p>Google Chrome Version 97.0.4692.99 on Windows is not clearing the session when restoring a tab from the history. This site
        has been created to test and demo that issue.</p>
    <ul>
        <li>When this page is first loaded (or the Update Session time button is pressed) the session variable with the
            time is updated from the server.
        </li>
        <li>Each time the browser is refreshed (F5) the current time is updated.</li>
        <li>When a new tab opened to this site, the there will be a new session storage and a new session time.</li>
    </ul>
    <h3>The Problem:</h3>
    <p>A new session is NOT create when restoring a tab from history.  To reproduce, using Google Chrome on Windows do the following:</p>
    <ol>
        <li>Make sure there is at least one other tab or instance of Chrome open, whether or not there is a website
            loaded is not relevant.
        </li>
        <li>Load this site and record the session time.</li>
        <li>Close the tab that is displaying this site.</li>
        <li>Open either a new tab or new instance of Chrome.</li>
        <li>Go to the browser History and restore the tab that was just closed.</li>
    </ol>
    <p>The session time is the same time recorded in step 2. As long as there is at least one instance of Chrome
        running, when restoring from history, the session will also be restored.</p>
    <p>This is not the way eitehr Edge or Firefox is currently working. Both created a new session. Which browser is
        working correctly?</p>
</div>
</body>
</html>