如何为Expo/React Native应用实现OpenSubtitles.com API认证?
OpenSubtitles.com API 安全认证方案(Expo/React Native)
核心思路
OpenSubtitles.com 支持 OAuth2 授权码流程,这是符合安全最佳实践的方案——用户无需向你的应用暴露账号密码,仅通过官方页面授权你的应用获取访问权限,最终你拿到的是短期有效的 JWT(Access Token)和用于刷新的 Refresh Token。
前置准备
- 前往 OpenSubtitles 开发者平台注册你的应用,获取 Client ID 和 Client Secret
- 配置应用的重定向 URI,Expo 环境下通常使用类似
exp://localhost:19000/--/auth/callback(需和代码中保持一致)
关键依赖
expo-auth-session:处理 OAuth2 授权的浏览器跳转、回调捕获expo-secure-store:安全存储 Refresh Token(比普通 AsyncStorage 更安全,加密存储)axios(或原生 fetch):发起 API 请求
实现步骤与代码示例
1. 配置 OAuth 授权参数
import * as AuthSession from 'expo-auth-session'; // OpenSubtitles OAuth 配置(端点需以官方文档为准) const discovery = { authorizationEndpoint: 'https://www.opensubtitles.com/api/v1/login', tokenEndpoint: 'https://www.opensubtitles.com/api/v1/token', }; const CLIENT_ID = '你的Client ID'; const REDIRECT_URI = 'exp://localhost:19000/--/auth/callback';
2. 发起授权请求
const startAuth = async () => { const result = await AuthSession.startAsync({ authUrl: `${discovery.authorizationEndpoint}?response_type=code&client_id=${CLIENT_ID}&redirect_uri=${encodeURIComponent(REDIRECT_URI)}`, }); if (result.type === 'success') { await exchangeCodeForToken(result.params.code); } else { // 处理授权失败(用户取消、参数错误等) console.log('授权失败:', result); } };
3. 用授权码换取 Token
import axios from 'axios'; import * as SecureStore from 'expo-secure-store'; const exchangeCodeForToken = async (code) => { try { const response = await axios.post(discovery.tokenEndpoint, { grant_type: 'authorization_code', code, client_id: CLIENT_ID, client_secret: '你的Client Secret', redirect_uri: REDIRECT_URI, }); const { access_token, refresh_token, expires_in } = response.data; // 安全存储 Refresh Token await SecureStore.setItemAsync('opensubtitles_refresh_token', refresh_token); // Access Token 存内存即可,无需持久化 global.opensubtitlesAccessToken = access_token; // 记录过期时间,提前触发刷新 global.tokenExpiry = Date.now() + expires_in * 1000; } catch (error) { console.error('换取Token失败:', error); } };
4. 刷新 Access Token
const refreshAccessToken = async () => { const refreshToken = await SecureStore.getItemAsync('opensubtitles_refresh_token'); if (!refreshToken) return null; try { const response = await axios.post(discovery.tokenEndpoint, { grant_type: 'refresh_token', refresh_token: refreshToken, client_id: CLIENT_ID, client_secret: '你的Client Secret', }); const { access_token, refresh_token, expires_in } = response.data; await SecureStore.setItemAsync('opensubtitles_refresh_token', refresh_token); global.opensubtitlesAccessToken = access_token; global.tokenExpiry = Date.now() + expires_in * 1000; return access_token; } catch (error) { // Refresh Token 失效,需重新引导用户授权 console.error('刷新Token失败:', error); await SecureStore.deleteItemAsync('opensubtitles_refresh_token'); return null; } };
5. 发起带认证的 API 请求
const fetchSubtitles = async () => { // 检查 Token 是否即将过期,提前刷新 if (Date.now() > global.tokenExpiry - 60 * 1000) { const newToken = await refreshAccessToken(); if (!newToken) { // Token 刷新失败,引导用户重新授权 startAuth(); return; } } try { const response = await axios.get('https://www.opensubtitles.com/api/v1/subtitles', { headers: { 'Api-Key': '你的应用API密钥', 'Authorization': `Bearer ${global.opensubtitlesAccessToken}` }, params: { // 按需添加请求参数 query: '你的搜索关键词' } }); return response.data; } catch (error) { console.error('API请求失败:', error); } };
注意事项
- 不要持久化 Access Token:Access Token 有效期短,存在内存即可,过期后用 Refresh Token 刷新
- SecureStore 仅存 Refresh Token:Refresh Token 有效期长,必须加密存储,避免泄露
- 异常处理要全面:比如 Refresh Token 失效、网络错误,需及时引导用户重新授权
- 确认官方端点:上述代码中的授权和 Token 端点需以 OpenSubtitles 官方最新文档为准
内容的提问来源于stack exchange,提问作者l00mad
相关产品推荐
相关产品推荐

