javascript - React 身份验证会话管理
问题描述
我正在开发这个由两部分组成的应用程序(React + Express... + Apollo(用于 GraphQL))。为了管理身份验证,我一直在阅读指南和观看视频,并且使用JWT 令牌和上下文 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
?我错过了什么?
任何意见表示赞赏。
解决方案
好吧...我一直在研究代码,并验证了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 官方页面并将其粘贴到那里。然后在验证签名字段中插入“秘密”或任何内容,它会将Invalid signature更改为Verified。
推荐阅读
- php - 在 PHP 中验证 GET 请求
- node.js - 从任何格式到所需格式的漂亮打印
- css - 原子中的CSS自动前缀,如何设置选项?
- javascript - 如何将单选按钮值传递给其他程序并使用 php 和 jquery 将值插入数据库
- java - 移动到另一个活动时仅显示黑屏
- jquery - 序列化不会在 jquery 中发送某些项目
- c# - 枚举的脚手架作为选择控件,但看不到任何值
- javascript - 无法写入 Firebase 数据库,出现空错误
- react-native - 使用 react-native-gps-state 库请求本地反应权限时如何修复应用程序崩溃?
- android - 在 android 8 中未收到云功能 FCM 消息