在 React Native 中实现 Token 认证是移动应用开发中的常见需求,它用于验证用户的身份并授权其访问受保护的 API 资源。
Token 认证的核心流程:
用户登录 (Login):
- 用户在前端输入用户名和密码。
- 前端将这些凭据发送到后端 API。
- 后端验证凭据。如果验证成功,后端会生成一个 Token(通常是 JWT - JSON Web Token)并返回给前端。
- 前端接收到 Token 后,将其安全地存储起来(例如使用
AsyncStorage
)。 - 前端更新用户的登录状态(例如 Context 或 Redux 中的状态)。
访问受保护资源 (Access Protected Resources):
- 前端需要访问后端受保护的 API 时,将之前保存的 Token 附带在请求的
Authorization
头中发送给后端。 - 后端接收到请求后,验证 Token 的有效性(例如检查签名、有效期、是否被吊销等)。
- 如果 Token 有效,后端处理请求并返回数据。
- 如果 Token 无效(过期、篡改等),后端会返回错误(通常是 401 Unauthorized 或 403 Forbidden),前端需要处理这些错误(例如引导用户重新登录)。
- 前端需要访问后端受保护的 API 时,将之前保存的 Token 附带在请求的
Token 刷新 (Token Refresh - 可选但推荐):
- 为了安全性,Token 通常有较短的有效期。
- 当访问 Token 过期时,前端可以使用一个 Refresh Token(通常有效期较长,也存储在客户端)向后端请求一个新的访问 Token。
- 后端验证 Refresh Token,如果有效,则签发新的访问 Token。
- 这减少了用户频繁重新登录的次数,同时保持了安全性。
用户登出 (Logout):
- 用户选择登出时,前端从
AsyncStorage
中移除保存的 Token。 - 前端清除用户相关的状态。
- (可选)通知后端使该 Token 失效(如果后端支持)。
- 用户选择登出时,前端从
React Native 中的实现细节:
我们将使用以下技术栈:
@react-native-async-storage/async-storage
: 用于在设备上持久化存储 Token。- Context API (或 Redux/Zustand 等状态管理库): 用于在整个应用中管理用户的登录状态和 Token。
fetch
API (或axios
): 用于进行网络请求。crypto-js
(可选,用于密码哈希): 在发送密码到后端前进行哈希处理,增加一层客户端安全性(尽管主要安全在于 HTTPS 和后端处理)。
详细代码实现与示例
我们将构建一个简化的应用,包含:
UserContext.tsx
: 管理用户登录状态和 Token。AuthScreen.tsx
: 登录界面。HomeScreen.tsx
: 登录后的主界面,可以访问受保护资源并登出。App.tsx
: 应用入口,根据登录状态决定显示哪个界面。
1. 安装必要的库
Bash
npm install @react-native-async-storage/async-storage crypto-js @react-navigation/native @react-navigation/stack
npx expo install react-native-screens react-native-safe-area-context # For react-navigation
# 或
yarn add @react-native-async-storage/async-storage crypto-js @react-navigation/native @react-navigation/stack
yarn expo install react-native-screens react-native-safe-area-context
2. 后端模拟 (Python Flask 示例)
为了能够运行前端代码,你需要一个简单的后端。这里提供一个 Python Flask 的极简示例。
backend/app.py
from flask import Flask, request, jsonify
from flask_cors import CORS
import jwt
import datetime
import hashlib # 用于密码哈希
app = Flask(__name__)
CORS(app) # 允许跨域请求
SECRET_KEY = "your_super_secret_key_for_jwt" # 生产环境请使用更复杂的密钥
REFRESH_SECRET_KEY = "your_super_secret_refresh_key_for_jwt"
# 简单模拟的用户数据库
users_db = {
"testuser": {
"password_hash": hashlib.sha256("testpassword".encode('utf-8')).hexdigest(), # 密码哈希
"user_id": "user123",
"user_name": "Test User",
"email": "test@example.com",
"phone": "123-456-7890"
}
}
@app.route('/api/user_login', methods=['POST'])
def user_login():
data = request.get_json()
username = data.get('username')
password_hash = data.get('password') # 接收前端传来的哈希后的密码
if not username or not password_hash:
return jsonify({"message": "用户名和密码不能为空"}), 400
user_data = users_db.get(username)
if user_data and user_data["password_hash"] == password_hash:
# 生成访问 Token (有效期短,例如 15 分钟)
access_token_payload = {
"user_id": user_data["user_id"],
"user_name": user_data["user_name"],
"exp": datetime.datetime.utcnow() + datetime.timedelta(minutes=15)
}
access_token = jwt.encode(access_token_payload, SECRET_KEY, algorithm="HS256")
# 生成刷新 Token (有效期长,例如 7 天)
refresh_token_payload = {
"user_id": user_data["user_id"],
"exp": datetime.datetime.utcnow() + datetime.timedelta(days=7)
}
refresh_token = jwt.encode(refresh_token_payload, REFRESH_SECRET_KEY, algorithm="HS256")
return jsonify({
"token": access_token,
"refresh_token": refresh_token, # 将 refresh_token 也返回
"user_id": user_data["user_id"],
"user_name": user_data["user_name"],
"email": user_data["email"],
"phone": user_data["phone"],
"message": "登录成功"
}), 200
else:
return jsonify({"message": "无效的用户名或密码"}), 401
@app.route('/api/protected_data', methods=['GET'])
def protected_data():
auth_header = request.headers.get('Authorization')
if not auth_header or not auth_header.startswith('Bearer '):
return jsonify({"message": "未授权:缺少或无效的Authorization头"}), 401
token = auth_header.split(" ")[1]
try:
payload = jwt.decode(token, SECRET_KEY, algorithms=["HS256"])
user_id = payload.get('user_id')
user_name = payload.get('user_name', '未知用户')
return jsonify({"message": f"欢迎, {user_name}! 这是受保护的数据。", "user_id": user_id}), 200
except jwt.ExpiredSignatureError:
return jsonify({"message": "未授权:Token 已过期"}), 401
except jwt.InvalidTokenError:
return jsonify({"message": "未授权:无效的 Token"}), 401
@app.route('/api/refresh_token', methods=['POST'])
def refresh_token():
data = request.get_json()
refresh_token = data.get('refresh_token')
if not refresh_token:
return jsonify({"message": "未授权:缺少刷新Token"}), 400
try:
payload = jwt.decode(refresh_token, REFRESH_SECRET_KEY, algorithms=["HS256"])
user_id = payload.get('user_id')
# 重新生成新的访问 Token
access_token_payload = {
"user_id": user_id,
"user_name": users_db.get(user_id, {}).get("user_name", "未知用户"), # 重新获取用户名
"exp": datetime.datetime.utcnow() + datetime.timedelta(minutes=15)
}
new_access_token = jwt.encode(access_token_payload, SECRET_KEY, algorithm="HS256")
# 也可以同时生成新的刷新 Token,以实现刷新 Token 的滚动更新
new_refresh_token_payload = {
"user_id": user_id,
"exp": datetime.datetime.utcnow() + datetime.timedelta(days=7)
}
new_refresh_token = jwt.encode(new_refresh_token_payload, REFRESH_SECRET_KEY, algorithm="HS256")
return jsonify({
"token": new_access_token,
"refresh_token": new_refresh_token # 返回新的刷新 Token
}), 200
except jwt.ExpiredSignatureError:
return jsonify({"message": "未授权:刷新 Token 已过期,请重新登录"}), 401
except jwt.InvalidTokenError:
return jsonify({"message": "未授权:无效的刷新 Token"}), 401
if __name__ == '__main__':
# 注意:10.0.2.2 是 Android 模拟器访问宿主机(开发机器)的默认 IP
# 如果是 iOS 模拟器或真实设备,请使用你电脑的局域网 IP
app.run(host='10.0.2.2', port=5000, debug=True)
运行后端:
- 保存为
backend/app.py
。 - 安装依赖:
pip install Flask Flask-Cors PyJWT
- 运行:
python backend/app.py
3. React Native 前端代码
src/contexts/UserContext.tsx
这是核心,负责管理用户认证状态、Token 的存储与获取。
// src/contexts/UserContext.tsx
import React, { createContext, useContext, useState, useEffect, ReactNode, useCallback } from 'react';
import AsyncStorage from '@react-native-async-storage/async-storage';
import { SHA256 } from 'crypto-js'; // 用于前端密码哈希
// 定义用户信息接口
export interface UserInfo {
user_id: string;
user_name: string;
email: string;
phone: string;
status: 'logged_in' | 'logged_out';
}
// 定义 Context 的类型
interface UserContextType {
user: UserInfo | null; // 当前登录的用户信息
authToken: string | null; // 访问 Token
refreshToken: string | null; // 刷新 Token
login: (username: string, password: string) => Promise<void>; // 登录方法
logout: () => void; // 登出方法
isLoading: boolean; // 是否正在加载用户数据 (应用启动时)
refreshAccessToken: () => Promise<boolean>; // 刷新访问 Token 的方法
}
// 创建 Context,并设置默认值
const UserContext = createContext<UserContextType>({
user: null,
authToken: null,
refreshToken: null,
login: async () => { /* no-op */ },
logout: () => { /* no-op */ },
isLoading: true, // 初始加载状态为 true
refreshAccessToken: async () => false, // 默认返回 false
});
// UserProvider 组件
const UserProvider = ({ children }: { children: ReactNode }) => {
const [user, setUser] = useState<UserInfo | null>(null);
const [authToken, setAuthToken] = useState<string | null>(null);
const [refreshToken, setRefreshToken] = useState<string | null>(null);
const [isLoading, setIsLoading] = useState(true);
// --- 1. 应用启动时加载存储的用户和 Token 信息 ---
useEffect(() => {
const loadStoredAuth = async () => {
try {
const storedAuthToken = await AsyncStorage.getItem('authToken');
const storedRefreshToken = await AsyncStorage.getItem('refreshToken');
const storedUserId = await AsyncStorage.getItem('userId');
const storedUserName = await AsyncStorage.getItem('userName'); // 假设也存储了用户名
if (storedAuthToken && storedRefreshToken && storedUserId && storedUserName) {
setAuthToken(storedAuthToken);
setRefreshToken(storedRefreshToken);
// 这里可以根据实际情况从 API 重新获取完整的用户信息,
// 或者直接使用存储的信息(如果它足够完整)
setUser({
user_id: storedUserId,
user_name: storedUserName,
email: 'N/A', // 示例,实际应从API获取或存储
phone: 'N/A', // 示例
status: 'logged_in',
});
console.log('User and tokens loaded from AsyncStorage.');
} else {
console.log('No existing user data or tokens found.');
setUser(null);
setAuthToken(null);
setRefreshToken(null);
}
} catch (error) {
console.error('Error loading user data from AsyncStorage:', error);
setUser(null);
setAuthToken(null);
setRefreshToken(null);
} finally {
setIsLoading(false); // 加载完成
}
};
loadStoredAuth();
}, []); // 空依赖数组,只在组件挂载时运行一次
// --- 2. 登录方法 ---
const login = useCallback(async (username: string, password: string) => {
try {
const hashedPassword = SHA256(password).toString(); // 客户端密码哈希
console.log('Hashed Password (Frontend):', hashedPassword);
const response = await fetch('http://10.0.2.2:5000/api/user_login', {
method: 'POST',
headers: {
'Content-Type': 'application/json',
},
body: JSON.stringify({ username, password: hashedPassword }),
});
if (!response.ok) {
const errorData = await response.json();
throw new Error(errorData.message || 'Login failed');
}
const data = await response.json();
if (!data.token || !data.refresh_token || !data.user_id) {
throw new Error('Invalid API response structure: missing token or user_id');
}
// 保存 Token 和用户 ID
await AsyncStorage.setItem('authToken', data.token);
await AsyncStorage.setItem('refreshToken', data.refresh_token);
await AsyncStorage.setItem('userId', data.user_id);
await AsyncStorage.setItem('userName', data.user_name); // 也存储用户名
// 更新 Context 状态
setAuthToken(data.token);
setRefreshToken(data.refresh_token);
setUser({
user_id: data.user_id,
user_name: data.user_name || username,
email: data.email || 'N/A',
phone: data.phone || 'N/A',
status: 'logged_in',
});
console.log('Login successful, user and tokens set.');
} catch (error) {
console.error('Login error:', error);
// 登录失败时清除所有状态
await logout(); // 调用登出方法清除所有相关信息
throw error; // 重新抛出错误以便调用方处理
}
}, []); // 空依赖数组,确保 login 函数的稳定性
// --- 3. 登出方法 ---
const logout = useCallback(async () => {
console.log('Logging out...');
await AsyncStorage.removeItem('authToken');
await AsyncStorage.removeItem('refreshToken');
await AsyncStorage.removeItem('userId');
await AsyncStorage.removeItem('userName');
setUser(null);
setAuthToken(null);
setRefreshToken(null);
console.log('User logged out, tokens cleared.');
}, []); // 空依赖数组,确保 logout 函数的稳定性
// --- 4. 刷新访问 Token 的方法 ---
const refreshAccessToken = useCallback(async (): Promise<boolean> => {
if (!refreshToken) {
console.warn('No refresh token available. Cannot refresh access token.');
await logout(); // 没有刷新 Token,视为需要重新登录
return false;
}
try {
console.log('Attempting to refresh access token...');
const response = await fetch('http://10.0.2.2:5000/api/refresh_token', {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({ refresh_token: refreshToken }),
});
if (!response.ok) {
const errorData = await response.json();
console.error('Failed to refresh token:', errorData.message);
await logout(); // 刷新失败,强制登出
return false;
}
const data = await response.json();
if (!data.token || !data.refresh_token) { // 后端可能也返回新的刷新token
console.error('Invalid refresh token response.');
await logout();
return false;
}
await AsyncStorage.setItem('authToken', data.token);
await AsyncStorage.setItem('refreshToken', data.refresh_token); // 保存新的刷新 Token
setAuthToken(data.token);
setRefreshToken(data.refresh_token); // 更新状态
console.log('Access token refreshed successfully.');
return true;
} catch (error) {
console.error('Error during token refresh:', error);
await logout(); // 网络或其他错误,强制登出
return false;
}
}, [refreshToken, logout]); // 依赖 refreshToken 和 logout,确保是最新的
// 提供的 Context 值
const contextValue = {
user,
authToken,
refreshToken,
login,
logout,
isLoading,
refreshAccessToken,
};
return (
<UserContext.Provider value={contextValue}>
{children}
</UserContext.Provider>
);
};
export default UserProvider;
// 自定义 Hook,方便组件使用 Context
export const useUser = () => useContext(UserContext);
src/screens/AuthScreen.tsx
登录界面,用户输入凭据并调用 login
方法。
// src/screens/AuthScreen.tsx
import React, { useState } from 'react';
import { View, Text, TextInput, Button, StyleSheet, ActivityIndicator, Alert } from 'react-native';
import { useUser } from '../contexts/UserContext'; // 引入 useUser Hook
function AuthScreen() {
const [username, setUsername] = useState('testuser'); // 默认用户名
const [password, setPassword] = useState('testpassword'); // 默认密码
const [isAuthenticating, setIsAuthenticating] = useState(false); // 登录状态
const { login } = useUser(); // 获取登录方法
const handleLogin = async () => {
setIsAuthenticating(true); // 开始登录
try {
await login(username, password); // 调用 Context 中的登录方法
// 登录成功,Context 会自动更新 user 状态,App.tsx 会负责导航
} catch (error: any) {
Alert.alert('登录失败', error.message || '请检查用户名和密码。');
console.error('Login error in AuthScreen:', error);
} finally {
setIsAuthenticating(false); // 结束登录
}
};
return (
<View style={styles.container}>
<Text style={styles.title}>用户登录</Text>
<TextInput
style={styles.input}
placeholder="用户名"
value={username}
onChangeText={setUsername}
autoCapitalize="none"
/>
<TextInput
style={styles.input}
placeholder="密码"
value={password}
onChangeText={setPassword}
secureTextEntry
/>
<Button
title={isAuthenticating ? "登录中..." : "登录"}
onPress={handleLogin}
disabled={isAuthenticating} // 登录中禁用按钮
/>
{isAuthenticating && <ActivityIndicator style={styles.spinner} size="small" color="#0000ff" />}
</View>
);
}
const styles = StyleSheet.create({
container: {
flex: 1,
justifyContent: 'center',
alignItems: 'center',
padding: 20,
backgroundColor: '#f5f5f5',
},
title: {
fontSize: 28,
fontWeight: 'bold',
marginBottom: 30,
color: '#333',
},
input: {
width: '100%',
padding: 15,
borderWidth: 1,
borderColor: '#ddd',
borderRadius: 8,
marginBottom: 15,
backgroundColor: '#fff',
},
spinner: {
marginTop: 10,
},
});
export default AuthScreen;
src/screens/HomeScreen.tsx
登录后的主界面,可以访问受保护资源并登出。
TypeScript
// src/screens/HomeScreen.tsx
import React, { useState, useEffect } from 'react';
import { View, Text, Button, StyleSheet, ActivityIndicator, Alert } from 'react-native';
import { useUser } from '../contexts/UserContext'; // 引入 useUser Hook
function HomeScreen() {
const { user, authToken, logout, refreshAccessToken } = useUser(); // 获取用户信息、token、登出、刷新方法
const [protectedData, setProtectedData] = useState<string | null>(null);
const [isFetchingData, setIsFetchingData] = useState(false);
// --- 访问受保护数据的方法 ---
const fetchProtectedData = async () => {
if (!authToken) {
Alert.alert('错误', '没有访问 Token,请重新登录。');
return;
}
setIsFetchingData(true);
try {
const response = await fetch('http://10.0.2.2:5000/api/protected_data', {
headers: {
'Authorization': `Bearer ${authToken}`, // 在 Authorization 头中携带 Token
},
});
if (response.status === 401) { // Unauthorized (Token 可能过期或无效)
const errorData = await response.json();
if (errorData.message === "未授权:Token 已过期") {
console.log('Access Token expired, attempting to refresh...');
const refreshed = await refreshAccessToken(); // 尝试刷新 Token
if (refreshed) {
// 如果刷新成功,可以考虑重新尝试本次请求 (更高级的实现会在拦截器中自动重试)
Alert.alert('提示', '访问 Token 已刷新,请重新尝试获取数据。');
} else {
Alert.alert('会话过期', '您的会话已过期,请重新登录。');
}
} else {
Alert.alert('认证失败', errorData.message || '无效的访问 Token。');
}
await logout(); // 遇到 401 且无法刷新,通常需要登出
return;
}
if (!response.ok) {
const errorData = await response.json();
throw new Error(errorData.message || '获取受保护数据失败。');
}
const data = await response.json();
setProtectedData(data.message);
} catch (error: any) {
Alert.alert('错误', `获取数据失败: ${error.message}`);
console.error('Error fetching protected data:', error);
} finally {
setIsFetchingData(false);
}
};
return (
<View style={styles.container}>
<Text style={styles.title}>欢迎,{user?.user_name || '用户'}!</Text>
<Text style={styles.info}>您的用户ID: {user?.user_id}</Text>
{authToken && (
<Text style={styles.info}>访问 Token: {authToken.substring(0, 20)}...</Text>
)}
{refreshToken && (
<Text style={styles.info}>刷新 Token: {refreshToken.substring(0, 20)}...</Text>
)}
<Button
title={isFetchingData ? "获取中..." : "获取受保护数据"}
onPress={fetchProtectedData}
disabled={isFetchingData || !authToken} // 没有 Token 或正在获取时禁用
/>
{isFetchingData && <ActivityIndicator style={styles.spinner} size="small" color="#0000ff" />}
{protectedData && (
<View style={styles.dataContainer}>
<Text style={styles.dataTitle}>受保护数据:</Text>
<Text style={styles.dataText}>{protectedData}</Text>
</View>
)}
<View style={styles.logoutButtonContainer}>
<Button title="登出" onPress={logout} color="red" />
</View>
</View>
);
}
const styles = StyleSheet.create({
container: {
flex: 1,
justifyContent: 'center',
alignItems: 'center',
padding: 20,
backgroundColor: '#f5f5f5',
},
title: {
fontSize: 24,
fontWeight: 'bold',
marginBottom: 10,
color: '#333',
},
info: {
fontSize: 16,
marginBottom: 5,
color: '#555',
},
spinner: {
marginTop: 10,
},
dataContainer: {
marginTop: 30,
padding: 15,
borderWidth: 1,
borderColor: '#ccc',
borderRadius: 8,
backgroundColor: '#e9e9e9',
width: '100%',
alignItems: 'center',
},
dataTitle: {
fontSize: 18,
fontWeight: 'bold',
marginBottom: 10,
color: '#333',
},
dataText: {
fontSize: 16,
textAlign: 'center',
color: '#666',
},
logoutButtonContainer: {
marginTop: 40,
width: '80%',
},
});
export default HomeScreen;
src/navigation/MainNavigator.tsx
(主应用内部导航)
TypeScript
// src/navigation/MainNavigator.tsx
import React from 'react';
import { createStackNavigator } from '@react-navigation/stack';
import HomeScreen from '../screens/HomeScreen';
// 你可以添加更多登录后的页面
const Stack = createStackNavigator();
function MainNavigator() {
return (
<Stack.Navigator screenOptions={{ headerShown: false }}>
<Stack.Screen name="Home" component={HomeScreen} />
{/* <Stack.Screen name="Settings" component={SettingsScreen} /> */}
</Stack.Navigator>
);
}
export default MainNavigator;
src/navigation/AuthNavigator.tsx
(认证页面导航)
// src/navigation/AuthNavigator.tsx
import React from 'react';
import { createStackNavigator } from '@react-navigation/stack';
import AuthScreen from '../screens/AuthScreen';
const Stack = createStackNavigator();
function AuthNavigator() {
return (
<Stack.Navigator screenOptions={{ headerShown: false }}>
<Stack.Screen name="Login" component={AuthScreen} />
{/* <Stack.Screen name="Register" component={RegisterScreen} /> */}
</Stack.Navigator>
);
}
export default AuthNavigator;
App.tsx
(应用入口)
这是应用的主文件,它设置了 UserProvider
并根据用户的登录状态来切换导航器。
TypeScript
// App.tsx
import 'react-native-gesture-handler'; // react-navigation 必备
import React from 'react';
import { NavigationContainer } from '@react-navigation/native';
import { createStackNavigator } from '@react-navigation/stack';
import { ActivityIndicator, View, StyleSheet, Text } from 'react-native';
import UserProvider, { useUser } from './src/contexts/UserContext'; // 引入你的 UserProvider 和 useUser Hook
import MainNavigator from './src/navigation/MainNavigator';
import AuthNavigator from './src/navigation/AuthNavigator';
const Stack = createStackNavigator();
// 根导航器,根据认证状态切换主/认证流程
function RootNavigator() {
const { user, isLoading } = useUser(); // 从 Context 获取 user 和 isLoading 状态
if (isLoading) {
// 如果正在加载用户数据,显示加载指示器或启动屏
return (
<View style={styles.loadingContainer}>
<ActivityIndicator size="large" color="#0000ff" />
<Text style={styles.loadingText}>正在加载应用数据...</Text>
</View>
);
}
return (
<Stack.Navigator screenOptions={{ headerShown: false }}>
{user ? (
// 如果 user 不为 null (已登录),显示主应用界面
<Stack.Screen name="Main" component={MainNavigator} />
) : (
// 如果 user 为 null (未登录),显示认证界面
<Stack.Screen name="Auth" component={AuthNavigator} />
)}
</Stack.Navigator>
);
}
export default function App() {
return (
<NavigationContainer>
<UserProvider>
<RootNavigator />
</UserProvider>
</NavigationContainer>
);
}
const styles = StyleSheet.create({
loadingContainer: {
flex: 1,
justifyContent: 'center',
alignItems: 'center',
backgroundColor: '#fff',
},
loadingText: {
marginTop: 10,
fontSize: 16,
color: '#555',
},
});
4. 项目结构
my-react-native-app/
├── App.tsx
├── package.json
├── src/
│ ├── contexts/
│ │ └── UserContext.tsx
│ ├── navigation/
│ │ ├── AuthNavigator.tsx
│ │ └── MainNavigator.tsx
│ └── screens/
│ ├── AuthScreen.tsx
│ └── HomeScreen.tsx
└── backend/ (你的后端代码,例如 app.py)
└── app.py
总结
UserProvider
: 是 Token 认证的核心。它负责在应用启动时从AsyncStorage
中加载 Token,管理登录/登出逻辑,并通过 Context API 将user
信息、authToken
、refreshToken
和isLoading
状态暴露给整个应用。isLoading
状态: 用于在应用启动时,等待UserProvider
从AsyncStorage
加载完数据。在这期间,你应该显示一个加载指示器,避免闪烁或提前渲染错误内容。login
方法: 用户登录成功后,从后端获取 Token 并将其保存在AsyncStorage
和 Context 状态中。logout
方法: 清除AsyncStorage
中的 Token 和 Context 状态。refreshAccessToken
方法: 处理访问 Token 过期的情况。它会尝试使用刷新 Token 获取新的访问 Token。- 网络请求: 在需要访问受保护资源时,从
useUser()
中获取authToken
,并将其添加到请求的Authorization: Bearer <token>
头中。 - 错误处理: 特别是对于 401/403 错误,需要捕获并引导用户重新认证(例如,跳转到登录页)。
这个示例提供了一个基本但完整的 React Native Token 认证流程。在生产环境中,你可能还需要考虑更复杂的错误处理、Token 有效性检查、安全性增强(如 HTTPS)、更强大的网络请求库(如 Axios 及其拦截器)以及更完善的用户信息管理。