React Native(Expo)结合Node.js实现Google登录的正确流程咨询
Expo + Node.js 集成Google登录的正确方案
一、项目结构建议
后端(Node.js)结构
src/config/:存放Google OAuth配置(客户端ID、客户端密钥、JWT密钥)routes/:处理登录相关路由(如/api/auth/google)services/:封装Google Token验证、用户逻辑(如googleAuthService.js)middleware/:可选,JWT验证中间件,用于后续API接口鉴权server.js:服务启动入口
前端(Expo)结构
components/:登录按钮组件(如GoogleLoginButton.js)utils/:封装本地存储、请求工具screens/:登录页面、首页等业务页面
二、安全的登录流程
- 客户端触发Google授权:通过
expo-auth-session获取授权ID Token(而非直接拉取用户信息) - 客户端将ID Token发送至Node.js后端
- 后端调用Google官方验证接口,校验ID Token的合法性(确保Token未被篡改、属于你的应用)
- 后端验证通过后,生成自身业务的JWT令牌返回给客户端,后续客户端用该JWT访问你的API
- 可选:后端根据Google返回的用户信息,创建/更新本地用户数据库记录
三、代码实现示例
前端(Expo)调整
import * as Google from "expo-auth-session/providers/google"; import { View, Button } from 'react-native'; import axios from 'axios'; import AsyncStorage from '@react-native-async-storage/async-storage'; import { useEffect } from 'react'; export default function GoogleLoginButton() { const [request, response, promptAsync] = Google.useAuthRequest({ androidClientId: "xxxxx.apps.googleusercontent.com", iosClientId: "xxxxx.apps.googleusercontent.com", // 指定获取ID Token,必须配置该参数 responseType: Google.ResponseType.IdToken, // 按需添加需要获取的用户信息权限 scopes: ['profile', 'email'], }); useEffect(() => { if (response?.type === 'success') { const { id_token } = response.params; // 将ID Token发送到后端验证 axios.post('http://你的后端地址/api/auth/google', { idToken: id_token }) .then(res => { // 保存后端返回的JWT到本地 AsyncStorage.setItem('userToken', res.data.token); // 跳转至首页或其他业务页面 }) .catch(err => { console.error('登录验证失败', err); }); } }, [response]); return ( <View> <Button title="Google登录" onPress={() => promptAsync()} disabled={!request} /> </View> ); }
后端(Node.js)实现
先安装依赖:npm install google-auth-library jsonwebtoken express cors
1. 配置文件(src/config/google.js)
module.exports = { clientId: '你的Google客户端ID(与前端一致)', clientSecret: '你的Google客户端密钥(Google开发者控制台获取)', jwtSecret: '自定义JWT密钥,用于生成业务令牌' };
2. 授权服务(src/services/googleAuthService.js)
const { OAuth2Client } = require('google-auth-library'); const config = require('../config/google'); const jwt = require('jsonwebtoken'); const client = new OAuth2Client(config.clientId); // 验证Google ID Token合法性 async function verifyGoogleIdToken(idToken) { const ticket = await client.verifyIdToken({ idToken: idToken, audience: config.clientId, // 确保Token是颁发给当前应用的 }); return ticket.getPayload(); // 返回用户信息:sub(Google唯一ID)、email、name等 } // 生成后端业务JWT function generateJwt(userData) { return jwt.sign( { userId: userData.sub, email: userData.email }, config.jwtSecret, { expiresIn: '7d' } // 设置令牌有效期 ); } module.exports = { verifyGoogleIdToken, generateJwt };
3. 登录路由(src/routes/auth.js)
const express = require('express'); const router = express.Router(); const { verifyGoogleIdToken, generateJwt } = require('../services/googleAuthService'); router.post('/google', async (req, res) => { try { const { idToken } = req.body; if (!idToken) { return res.status(400).json({ message: '缺少ID Token' }); } // 验证Google Token const userPayload = await verifyGoogleIdToken(idToken); // 此处可根据userPayload.sub查询本地数据库,无则创建用户 // 示例:假设已完成用户查询/创建逻辑 const user = { id: userPayload.sub, email: userPayload.email, name: userPayload.name }; // 生成后端业务令牌 const token = generateJwt(userPayload); res.json({ token, user }); } catch (err) { console.error('验证失败', err); res.status(401).json({ message: 'Google登录验证失败' }); } }); module.exports = router;
4. 服务启动文件(server.js)
const express = require('express'); const authRoutes = require('./src/routes/auth'); const cors = require('cors'); const app = express(); // 跨域支持,适配Expo客户端与后端的跨域请求 app.use(cors()); // 解析JSON请求体 app.use(express.json()); // 挂载登录路由 app.use('/api/auth', authRoutes); const PORT = process.env.PORT || 3000; app.listen(PORT, () => { console.log(`服务器运行在端口 ${PORT}`); });
四、关键注意事项
- 禁止客户端直接使用用户信息做业务判断:客户端数据可被篡改,必须经过后端验证后再处理
- Google客户端密钥需保密:仅存放在后端,绝对不能暴露在前端代码中
- JWT鉴权:后续客户端访问API时,需在请求头携带
Authorization: Bearer <后端JWT令牌>,后端通过中间件验证令牌合法性 - 生产环境强制HTTPS:防止Token在传输过程中被窃听
内容的提问来源于stack exchange,提问作者abankitbaid
相关产品推荐
相关产品推荐

