React 身份验证会话管理
Posted
技术标签:
【中文标题】React 身份验证会话管理【英文标题】:React Authentication session management 【发布时间】:2019-12-28 20:50:44 【问题描述】:我正在开发这个由两部分组成的应用程序(React + Express... + Apollo(用于 GraphQL))。 为了管理身份验证,我一直在阅读指南和观看视频,并使用JWT token 和Context API 取得了一些进展,例如:
-
每当用户登录时,React 都会使用 gql(由 apollo-boost 提供支持)向 Express 发出请求。
Express 服务器(后端)向其发送响应,然后使用解析器处理其数据:
require('dotenv').config();
import User from '../models/User';
import bcrypt from 'bcrypt';
import jwt from 'jsonwebtoken';
export default
Query:
login: async (_, email, password , req) =>
try
const user = await User.findOne( email );
if (!user)
throw new Error('User don\'t exist');
const isEqual = await bcrypt.compare(password, user.password);
if (!isEqual)
throw new Error('Wrong credentials');
const
JWTSECRET = 'secret'
= process.env;
const token = jwt.sign( userId: user.id, email: user.email , JWTSECRET,
expiresIn: '1h'
);
return
userId: user.id,
token,
tokenExpiration: 3600000 // Date.now()+3600000 doesn't work cause Int is 32-bit signed
catch (e)
throw new Error(e.message);
;
const hashPassword = async password =>
const saltRounds = 10;
const hashedPassword = await new Promise((resolve, reject) =>
try
bcrypt.hash(password, saltRounds, (err, hash) =>
if (err) reject(err);
resolve(hash);
);
catch (e)
throw new Error(e.message);
);
return hashedPassword;
;
-
React 获取响应并将其附加到上下文中,我猜它还会分配一些
localStorage
的东西来保持会话,例如:
import React from 'react';
import AuthContext from './auth-context';
import ApolloClient, gql from 'apollo-boost';
function getClient()
return new ApolloClient( uri: 'http://localhost:4000/ws', credentials: 'same-origin' );
const AuthState = props =>
const defaultValue =
userId: null,
userFirstName: null,
userLastName: null,
userEmail: null,
token: null,
tokenExpiration: null,
createdOn: null,
errors: [],
login,
get,
;
return (
<AuthContext.Provider value=defaultValue>
props.children
</AuthContext.Provider>
);
/**
* Login
* @param string email
* @param string password
*/
async function login(email, password)
const client = getClient();
const CHECK_CREDENTIALS = gql`
query
login(email: "$email", password: "$password")
userId
token
tokenExpiration
`;
const data, loading, errors = await client.query(
query: CHECK_CREDENTIALS,
errorPolicy: 'all'
);
if (loading)
defaultValue.message = "Validating credentials";
if (errors)
defaultValue.errors = errors.map(error => <p>error.message</p>);
if (data && data.login)
defaultValue.userId = data.login.userId;
defaultValue.token = data.login.token;
defaultValue.tokenExpiration = data.login.tokenExpiration;
// Persist session ?
localStorage.setItem('user-token', data.login.token);
localStorage.setItem('user-token-expiration', data.login.tokenExpiration);
async function get(userId)
const client = getClient();
const GET_USER = gql`
query
user(_id: "$userId")
firstName
lastName
email
`;
const data, loading, errors = await client.query(
query: GET_USER,
errorPolicy: 'all'
);
if (loading)
defaultValue.message = "Getting user";
if (errors)
defaultValue.errors = errors.map(error => <p>error.message</p>);
if (data && data.user)
defaultValue.userFirstName = data.user.firstName;
defaultValue.userLastName = data.user.lastName;
defaultValue.userEmail = data.user.email;
export default AuthState;
-
在每个需要身份验证的组件中,我都会检查
context.token
和 context.tokenExpiration
并重定向到登录或让它们通过。
我一直想知道如果我使用开发者控制台手动设置一些user-token
和user-token-expiration
会怎样?我错过了什么?
感谢任何cmets。
【问题讨论】:
你的代码有效吗? @Peter,我还没有尝试使用localStorage
管理,但如果不使用它,它会在我重新加载页面之前工作。
【参考方案1】:
嗯...我一直在研究代码,并验证了 login
解析器使用秘密签名来验证令牌。
login: async (_, email, password , req) =>
try
const user = await User.findOne( email );
if (!user)
throw new Error('wrong credentials');
const isEqual = await bcrypt.compare(password, user.password);
if (!isEqual)
throw new Error('wrong credentials');
const
JWTSECRET = 'secret'
= process.env;
const token = jwt.sign( userId: user.id, email: user.email , JWTSECRET,
expiresIn: '1h'
);
return
userId: user.id,
token,
tokenExpiration: Date.now() + 3600000
catch (e)
throw new Error(e.message);
为了看到实际情况,在生成令牌后转到JWT.IO Official page 并将其粘贴到那里。然后在 Verify signature 字段中插入“秘密”或任何内容,它会将 Invalid signature 更改为 Verified。
【讨论】:
以上是关于React 身份验证会话管理的主要内容,如果未能解决你的问题,请参考以下文章
会话管理:如何为 REST 服务生成身份验证令牌? (球衣)
如何使用 MongoDB + NodeJS Express 向 ReactJS React Router 和 Redux 添加登录身份验证和会话?