React Native中JWT认证出现Invalid Token Specified问题求助
JWT认证报错
Invalid Token Specified排查及React Native学习建议 问题背景
将React Web项目迁移至React Native开发移动端应用,GraphQL查询及mutation功能正常,但JWT令牌认证失效,系统提示Invalid Token Specified。
相关代码
apolloClient.js
import { ApolloClient } from "@apollo/client/core"; import { InMemoryCache } from "@apollo/client/cache"; import { setContext } from "@apollo/client/link/context"; import { createUploadLink } from "apollo-upload-client"; import AsyncStorage from "@react-native-async-storage/async-storage"; const httpLink = createUploadLink({ uri: "http://localhost:5001/graphql", }); const authLink = setContext(async () => { const token = await AsyncStorage.getItem("jwtToken"); return { headers: { Authorization: token ? `Bearer ${token}` : "", }, }; }); const client = new ApolloClient({ link: authLink.concat(httpLink), cache: new InMemoryCache(), }); export default client;
auth.js
import { useReducer, createContext, useEffect } from "react"; import jwtDecode from "jwt-decode"; import AsyncStorage from "@react-native-async-storage/async-storage"; const initialState = { user: null, }; const getToken = async () => { try { const storedToken = await AsyncStorage.getItem("jwtToken"); if (storedToken !== null) { const decodedToken = jwtDecode(AsyncStorage.getItem("jwtToken")); const expirationData = decodedToken.exp; const currentDate = Date.now / 1000; if (expirationData >= currentDate) { initialState.user = decodedToken; } } } catch (err) { console.log(err); } }; getToken(); const AuthContext = createContext({ user: null, login: (userData) => {}, logout: () => {}, }); function authReducer(state, action) { switch (action.type) { case "LOGIN": return { ...state, user: action.payload, }; case "LOGOUT": return { ...state, user: null, }; default: return state; } } function AuthProvider(props) { const [state, dispatch] = useReducer(authReducer, initialState); function login(userData) { AsyncStorage.setItem("jwtToken", userData.token); dispatch({ type: "LOGIN", payload: userData, }); } function logout() { AsyncStorage.removeItem("jwtToken"); dispatch({ type: "LOGOUT" }); } useEffect(() => { if (state.user) { const decodedToken = jwtDecode(AsyncStorage.getItem("jwtToken")); const timeLeft = decodedToken.exp * 1000 - Date.now(); setTimeout(() => { dispatch({ type: "LOGOUT", }); AsyncStorage.removeItem("jwtToken"); }, timeLeft); } }, [state]); return ( <AuthContext.Provider value={{ user: state.user, login, logout }} {...props} /> ); } export { AuthContext, AuthProvider };
App.js
import React from 'react'; import { ApolloProvider } from '@apollo/client'; import { NavigationContainer } from '@react-navigation/native'; import { createStackNavigator } from '@react-navigation/stack'; import { useFonts } from 'expo-font'; import { MyShop, SignIn, SignUp, Navigator, Chats, ShoppingCart, Settings, Me, Home, Categories, SubCategories, Products, } from './screens'; import { AuthProvider } from './context/auth'; import client from './context/apolloClient'; export default function App() { LogBox.ignoreAllLogs(true); const Stack = createStackNavigator(); return ( <ApolloProvider client={client}> <AuthProvider> <NavigationContainer> <Stack.Navigator screenOptions={{ headerShown: false }} initialRouteName="Navigator" > <Stack.Screen name="Navigator" component={Navigator} options={{ gestureEnabled: false }} /> <Stack.Screen name="Home" component={Home} options={{ gestureEnabled: false }} /> <Stack.Screen name="SignIn" component={SignIn} options={{ gestureEnabled: false }} /> <Stack.Screen name="SignUp" component={SignUp} options={{ gestureEnabled: false }} /> <Stack.Screen name="Me" component={Me} options={{ gestureEnabled: false }} /> <Stack.Screen name="MyShop" component={MyShop} /> <Stack.Screen name="ShoppingCart" component={ShoppingCart} /> <Stack.Screen name="Chats" component={Chats} /> <Stack.Screen name="Settings" component={Settings} /> {/* Other Pages */} <Stack.Screen name="Categories" component={Categories} /> <Stack.Screen name="SubCategories" component={SubCategories} /> <Stack.Screen name="Products" component={Products} /> </Stack.Navigator> </NavigationContainer> </AuthProvider> </ApolloProvider> ); }
报错信息

问题排查与修复
核心错误点
异步操作处理不当
getToken是异步函数,直接调用无法等待其执行完成,导致initialState.user初始化时无法正确赋值。getToken及useEffect中,调用jwtDecode(AsyncStorage.getItem("jwtToken"))时,AsyncStorage.getItem是异步方法,返回Promise而非token字符串,直接传给jwtDecode会导致解码失败,生成无效的token数据。Date.now是函数,错误写成Date.now / 1000,导致currentDate为函数引用,与expirationData的对比永远不成立。
状态初始化逻辑错误
直接修改initialState无法触发组件状态更新,应该通过dispatch来更新用户状态。
修正后的auth.js关键代码
function AuthProvider(props) { const [state, dispatch] = useReducer(authReducer, initialState); // 初始化验证token useEffect(() => { const initAuth = async () => { try { const storedToken = await AsyncStorage.getItem("jwtToken"); if (storedToken) { const decodedToken = jwtDecode(storedToken); const currentDate = Date.now() / 1000; if (decodedToken.exp >= currentDate) { dispatch({ type: "LOGIN", payload: decodedToken }); // 设置过期自动登出定时器 const timeLeft = decodedToken.exp * 1000 - Date.now(); setTimeout(logout, timeLeft); } else { await AsyncStorage.removeItem("jwtToken"); } } } catch (err) { console.error(err); await AsyncStorage.removeItem("jwtToken"); } }; initAuth(); }, []); function login(userData) { AsyncStorage.setItem("jwtToken", userData.token); const decodedToken = jwtDecode(userData.token); dispatch({ type: "LOGIN", payload: decodedToken }); // 设置过期定时器 const timeLeft = decodedToken.exp * 1000 - Date.now(); setTimeout(logout, timeLeft); } // ... logout函数及其他代码保持不变 }
额外检查项
- 确认
apolloClient.js中Authorization头格式符合后端要求,Bearer后需有空格,且token无多余空格或换行。 - 登录时验证
userData.token是后端返回的完整JWT字符串,未被截断或篡改。
React Native学习建议
- 夯实基础:优先掌握RN核心组件(View、Text、ScrollView等)和原生API(AsyncStorage、Permissions等),熟悉Flexbox布局的跨平台特性。
- 状态管理循序渐进:从Context API入门,熟练后再尝试Redux、Zustand等工具,避免过度复杂化。
- 掌握调试技巧:用RN Debugger配合Chrome DevTools调试网络请求,查看请求头是否正确携带token;用
console.log快速验证变量值。 - 关注平台差异:iOS与Android在组件样式、系统API上存在差异,学会用
Platform模块处理适配逻辑。 - 利用生态工具:用Expo简化开发、打包流程;熟练使用React Navigation实现路由管理,这是RN生态的标准导航方案。
- 迁移注意事项:RN无DOM环境,Web项目中的
document、window等API无法使用,需替换为RN的对应实现。
内容的提问来源于stack exchange,提问作者Mohammad Khan
相关产品推荐
相关产品推荐

