node.js - http-only cookie + token:双重工作?
问题描述
亲爱的,
我试图找到如何使用服务器发送的仅 hhtp cookie 在客户端管理身份验证。
我不明白的是,由于前端无法访问仅 HTTP 的 cookie,前端如何知道用户(仍然)经过身份验证?
到目前为止,唯一的解决方案是在身份验证成功时向客户端发送令牌。并将此令牌保存在客户端创建的第二个 cookie 中。
但在我看来,我做了两次同样的工作。
1-在服务器端管理仅 HTTP cookie,尤其是到期日期 2-也在客户端管理第二个 cookie 的到期日期。
怎样才能避免这种情况?我想基于 HTTP only server cookie 管理客户端的身份验证。如果有服务器 cookie,则继续,否则重定向到登录页面。
我在服务器端使用 node/express 并对客户端做出反应。会话存储在redis中,双方都是HTTPS使用证书。
谢谢
解决方案
您不需要存储另一个 cookie。我想您在端点上使用基于令牌的身份验证,例如。智威汤逊。然后你想想这个场景:
- 用户将用户名/密码发送到服务器。
- 检查用户凭据,如果有效,则使用令牌创建仅限 http 的 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,
});
- 编写 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();
};
- 检查每个请求的用户 ID。如果没有userId,用户没有登录
if (!req.userId) {
throw new AuthenticationError('Log in!');
}
- 如果用户的令牌无效/过期,您将收到 AuthenticationError。抓住它并重定向到登录页面。
- 如果您的 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;
如果我们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>
推荐阅读
- amazon-web-services - 防止 AWS Autoscalingroup 在通过 Cloudformation 更新期间启动超过最大大小的实例
- python - 在光栅中链接虚拟文件系统驱动程序
- azure - Azure 数据工厂:访问驻留在虚拟网络中的 Web 服务
- postman - 我们如何使用 postman 测试 API 的多个场景?
- php - 从数组中的 mySQL 表中检索数据
- python - 在另一个字典中附加一个字典并解析为 JSON
- c# - .NET Core 解决方案版本
- pytorch - pytorch中的内存高效批量成对KL散度
- python - 连接字节以列出 Python 3 的不一致行为
- java - Spring Boot Rest API 安全性