我正在使用localstorage在react/redux应用程序中存储一个jwt用于身份验证。如果用户的令牌过期,我会尝试让用户注销。一种方法是在后端使用authMiddleware.js发送error.message
,并将有效负载设置为一个错误变量。然后在useEffect中,如果错误是jwt expired
,我运行logout函数(它只清除本地存储),并将错误重置为null。如下所示:
authMiddleware.js:
const jwt = require("jsonwebtoken");
const User = require("../models/user");
const protect = async (req, res, next) => {
let token = req.body.token;
try {
const decoded = jwt.verify(token, process.env.JWT_SECRET);
req.user = await User.findById(decoded.id).select("-password");
next();
} catch (error) {
res.status(400).json({ message: error.message });
}
};
module.exports = { protect };
Portfolio.slice:
export const getCurrentHoldings = createAsyncThunk(
"/portfolio/getCurrentHoldings",
async (value, thunkAPI) => {
try {
const token = thunkAPI.getState().auth.user.token;
const userID = thunkAPI.getState().auth.user._id;
const newObj = {
token: token,
userID: userID,
};
let url = `http://localhost:3001/api/portfolio/getCurrentHoldings`;
const response = await axios.post(url, newObj);
console.log("New request ran in getCurrentHoldings");
return response.data;
} catch (error) {
const message =
(error.response &&
error.response.data &&
error.response.data.message) ||
error.message ||
error.toString();
return thunkAPI.rejectWithValue(message);
}
}
);
const initialState = {
error: null,
status: "idle",
holdings: null,
jwtError: null,
};
export const portfolioSlice = createSlice({
name: "portfolio",
initialState,
reducers: {
reset: (state) => initialState,
},
extraReducers(builder) {
builder
.addCase(getCurrentHoldings.pending, (state, action) => {
state.status = "loading";
})
.addCase(getCurrentHoldings.fulfilled, (state, action) => {
state.status = "success";
state.holdings = action.payload;
console.log(state.holdings);
})
.addCase(getCurrentHoldings.rejected, (state, action) => {
state.status = "failed";
state.error = action.error.message;
state.jwtError = action.payload;
})
},
});
Portfolio.js:
useEffect(() => {
if (jwtError == "jwt expired") {
dispatch(logout());
dispatch(reset());
}
}, [jwtError]);
这个解决方案的问题是我有多个切片,我需要为每个切片添加一个类似的变量,useEffect将增长并开始看起来像:
useEffect(() => {
if (jwtError == "jwt expired") {
dispatch(logout());
dispatch(reset());
}
if (jwtError1 == "jwt expired") {
dispatch(logout());
dispatch(reset1());
}
if (jwtError2 == "jwt expired") {
dispatch(logout());
dispatch(reset2());
}
}, [jwtError, jwtError1, jwtError2]);
因此,该解决方案是不可扩展的,我认为解决这个问题的一个方法是让一些切片从另一个切片访问数据,这样至少useEffect会减少到原来的大小,并且是可伸缩的,但是我发现reducers only have access to the state they own。因此,进一步研究这个问题,我发现了一些与此相关的帖子,我得到了建议,要么1. use cookies instead of localstate 2. Use middleware和3. use instance.interceptors
现在,我对上述所有解决方案的一个问题是,这个问题是否应该在前端、后端或两者上解决?因为中间件和示例拦截器解决方案看起来像是在前端解决的。我想知道这是否是一个安全风险,以及是否也应该使用后端中间件。
我还想知道使用cookie而不是useState是否只是一个最佳实践,但无论哪种方式,我都想用localstorage来实现这一点。
最后,我想知道如何在react中使用redux来实现这一点的最佳实践,以及在我的设置中代码可能是什么样子的。
更新:我目前尝试的解决方案是redux中间件,我无法在前端解码令牌,在react项目中安装jsonwebtoken会导致一个错误:Module not found: Error: Can't resolve 'crypto' in myfile
。据我所知,如果我要按照中间件链接中的建议解码它,我将需要在前端使用此库。
1条答案
按热度按时间gc0ot86w1#
因此,我对这个问题进行了更多的研究,发现了一些与此相关的帖子,我得到了以下建议:1.使用cookie而不是localstate; 2.使用中间件; 3.使用示例拦截器
你得到的建议很好,我肯定会使用一个只在http上的cookie来存储令牌(因为与JS运行时分离,所以更安全,没有恶意的js代码可以看到它)* 和 * redux中间件 * 和 * axios拦截器。
我目前正在尝试的解决方案是redux中间件,我无法在前端解码令牌,在react项目中安装jsonwebtoken会导致错误:找不到模块:错误:无法解析myfile中的“crypto”。据我所知,如果要按照中间件链接中的建议解码,我将需要在前端使用此库。
如果您使用的是
https://www.npmjs.com/package/jsonwebtoken
,这似乎是一个仅用于Node.js的实现,并不适用于浏览器。看一下JWT Verify client-side?,建议您在浏览器中使用https://github.com/auth0/jwt-decode
就足够了。如果您不使用仅基于HTTP cookie的解决方案,还有一个更优雅的解决方案:您可以对JWT进行解码,读取过期时间,然后安排函数在过期时间之前几秒钟运行(通过
setInterval
),它刷新令牌。如果失败,函数可以调度一个操作,注销用户并将redux状态重置为您需要的状态。这是一个更主动的解决方案,因为您不需要等到对后端的请求由于令牌过期而失败-毕竟您知道令牌何时过期。