大家好,我正在开发一个 Mern 应用程序,并且很好奇如何实现 jwt 令牌身份验证,它工作正常,但我前面遇到了一个小问题,用户登录后令牌在本地存储上成功设置,但是当我尝试在标头中发送令牌时使用 axios 获取值,第一次渲染时令牌值始终为空,我必须刷新页面,以便标头可以从本地存储访问令牌 这是useLogin组件,令牌立即设置在localStorage上
const login = async (email, password) => {
const user = { email, password };
setError(null);
try {
const response = await axios.post(
"http://localhost:4000/api/user/login",
user
);
const result = response.data;
console.log(response);
if (response.statusText === "OK") {
setError(null);
setIsLoading(false);
// save the token in local storage
localStorage.setItem("user", JSON.stringify(result));
// update the dispatch
dispatch({ type: "LOGIN", payload: result });
}
} catch (error) {
const response = error.response.data;
setError(response.message);
//console.log(error);
}
};
这是我的 authContext,我试图在其中获取 localStorage 值
export const AuthContext = createContext();
export const authReducer = (state, action) => {
switch (action.type) {
case "LOGIN":
return { user: action.payload.user };
case "LOGOUT":
return { user: null };
default:
return state;
}
};
export const AuthContextProvider = ({ children }) => {
const initialState = JSON.parse(localStorage.getItem("user")) || null;
const [state, dispatch] = useReducer(authReducer, { user: initialState });
// useEffect(() => {
// //check if there is a token in local storage
// const user = JSON.parse(localStorage.getItem("user"));
// console.log(user);
// if (user !== undefined && user !== null) {
// dispatch({ type: "LOGIN", payload: { user } });
// }
// }, []);
console.log("authContext state", state);
return (
<AuthContext.Provider value={{ ...state, dispatch }}>
{children}
</AuthContext.Provider>
);
};
我尝试使用 useEffect ,它导致了同样的问题, getItem("user") 在第一次渲染时始终为空,并且在我刷新页面后工作正常
最后是我尝试获取数据的主组件
user.token 变量在第一次渲染时始终未定义,但在刷新时工作正常
import axios from "axios";
import { useWorkoutContext } from "../hooks/useWorkoutContext";
import { useAuthContext } from "../hooks/useAuthContext";
// component
import WorkoutDetails from "../components/workoutDetails";
import WorkoutForm from "../components/workoutForm";
const Home = () => {
// const [workouts, setWorkouts] = useState([]);
const { workouts, dispatch } = useWorkoutContext();
const [error, setError] = useState(null);
const { user } = useAuthContext();
useEffect(() => {
const fetchWorkouts = async () => {
console.log(user.token);
try {
if (user && user.token) {
const response = await axios.get(
"http://localhost:4000/api/workouts",
{
headers: {
Authorization: `Bearer ${user.token}`,
},
}
);
const result = response.data;
if (response.status === 200) {
// console.log(result);
// setWorkouts(result);
dispatch({ type: "SET_WORKOUTS", payload: result });
}
}
} catch (error) {
console.error("Error fetching workouts:", error);
setError("Error fetching workouts. Please check your network.");
}
};
//console.log(user);
if (user) {
fetchWorkouts();
}
}, [dispatch, user]);
return (
<div className="home">
<div className="workouts">
{workouts &&
workouts.map((workout) => {
return <WorkoutDetails key={workout._id} workout={workout} />;
})}
{error && <p>{error}</p>}
</div>
<WorkoutForm />
</div>
);
};
export default Home;
这是我的返回 requireAuth 快速代码
import { userModel } from "../models/userModel.js";
const requireAuth = async (req, res, next) => {
// verify authentication
// we get authorization from the headers
const { authorization } = req.headers;
console.log(authorization);
if (!authorization) {
res.status(401).json({ error: "authorization required" });
}
// authorization is a string that contains the token
let token;
if (authorization) {
token = authorization.split(" ")[1];
console.log("Token is " + token);
}
try {
// returns the id of the user that is in the token
const { _id } = jwt.verify(token, process.env.SECRET);
console.log("id is " + _id);
req.user = await userModel.findOne({ _id }).select("_id");
next();
} catch (error) {
console.log(error);
res.status(401).json({ error: "request is not authorized" });
}
};
export default requireAuth;
我已经坚持了两天了:'((
发生这种情况是因为您的响应由
email
、token
和 user
对象组成。您将整个响应存储在本地存储中,因此您存储了一个由 email
、token
和 user
组成的对象。但是,当您处理 LOGIN
事件时,您只存储响应中的 user
对象和其他两个键(email
和 token
被丢弃)。
在分派 LOGIN 事件后的第一次渲染中,您尝试访问
token
键,但它不在用户对象中 - 它在用户对象之外,并且您尚未将其保存在减速器中。
刷新页面时,您会解析本地存储中的内容,在初始状态下,您将获得完整的响应,包括
email
、token
和 user
对象。当您使用解析后的对象并请求 token
值时,它现在可以访问。
您需要进行以下更改:
export const authReducer = (state, action) => {
switch (action.type) {
case "LOGIN":
return { user: action.payload}; // save the whole payload instead of just the user object (action.payload.user)
case "LOGOUT":
return { user: null };
default:
return state;
}
};