http-only cookie + 令牌:双重工作?

Jer*_*me 3 cookies https node.js express reactjs

亲爱的,

我试图找到如何使用服务器发送的 hhtp-only cookie 在客户端管理身份验证。

我不明白的是,由于前端无法访问 HTTP only cookie,前端如何知道用户(仍然)已通过身份验证?

到目前为止,找到的唯一解决方案是在身份验证成功时向客户端发送令牌。并将此令牌保存在客户端创建的第二个 cookie 中。

但在我看来,我在做同样的工作两次。

1- 在服务器端管理仅 HTTP cookie,尤其是到期日期 2- 在客户端也管理第二个 cookie 的到期日期。

如何避免这种情况?我想根据仅 HTTP 服务器 cookie 管理客户端的身份验证。如果有服务器cookie,则继续,否则重定向到登录页面。

我在服务器端使用 node/express 并对客户端做出反应。session存储在redis中,双方都是使用证书的HTTPS。

谢谢

lan*_*rid 8

您不需要存储另一个 cookie。我想您在端点上使用基于令牌的身份验证,例如。智威汤逊。那你想想这个场景:

  1. 用户将用户名/密码发送到服务器。
  2. 检查用户凭据,如果有效,则使用令牌创建 http-only cookie
    const user = await getUser({ where: { email } });

    const valid = await bcrypt.compare(password, user.password);
    if (!valid) {
      throw new UserInputError('Form Arguments invalid', {
        invalidArgs: {
          'password': 'Invalid password!',
        },
      });
    }

    const token = jwt.sign({ userId: user.id }, process.env.APP_SECRET);
    /
    res.cookie('token', token, {
      httpOnly: true,
      maxAge: 1000 * 60 * 60 * 24 * 365,
    });

Run Code Online (Sandbox Code Playgroud)
  1. 编写 auth 中间件,将 userId 放到 req 上,以便将来访问请求
const jwt = require('jsonwebtoken');
const { AuthenticationError } = require('apollo-server');

module.exports = async function(req, res, next) {
  const { token } = req.cookies;

  if (token) {
    try {
      const { userId } = jwt.verify(token, process.env.APP_SECRET);

      if (!userId) return next();
      req.userId = userId;

    } catch (e) {
      console.log(e);
    }
  }

  next();
};
Run Code Online (Sandbox Code Playgroud)
  1. 检查每个请求的 userId。如果没有userId,则用户未登录
  if (!req.userId) {
     throw new AuthenticationError('Log in!');
   }
Run Code Online (Sandbox Code Playgroud)
  1. 如果用户的令牌无效/过期,您将收到 AuthenticationError。抓住它并重定向到登录页面。
  2. 如果您的 UI 取决于用户状态,您可以创建易于使用的组件(我正在使用 React)来检查它。

用户组件:

import { Query } from 'react-apollo';
import gql from 'graphql-tag';
import PropTypes from 'prop-types';

const CURRENT_USER_QUERY = gql`
  query CURRENT_USER_QUERY {
    me {
      userId
      firstName
      lastName
      profilePictureUrl
    }
  }
`;

const User = props => (
  <Query {...props} query={CURRENT_USER_QUERY} fetchPolicy={'cache-first'}>
    {payload => props.children(payload)}
  </Query>
);

User.propTypes = {
  children: PropTypes.func.isRequired,
};

export default User;

Run Code Online (Sandbox Code Playgroud)

如果我们me从服务器获取对象,您知道,有一个登录用户,因此您可以根据用户的状态进行渲染:

import { Link } from 'react-router-dom';
import React from 'react';

<User>
  {({ loading, error, data: { me } }) => {
    if (loading || error || !me) return (
      <Button component={Link} to={'/login'}>Login</Button>
    );
    if(me) return (
      <Button component={Link} to={'/dashboard'}>Go to dashboard</Button>
    )
  }}
</User>
Run Code Online (Sandbox Code Playgroud)