React Native登录页面如何对接MySQL后端route.js做账号验证?
问题:React Native登录页面对接MySQL后端实现账号密码验证
我是React Native新手,正尝试实现登录页面与MySQL后端数据库的连接。目前已有可调用数据库数据的route.js文件,但不清楚如何在登录页面获取用户数据以进行账号密码验证。我已查阅大量资料,但相关解决方案不够清晰,仅得到“我的可以运行”这类无帮助的回复。附上相关代码文件,寻求有效解决思路,拒绝“我的可以运行”这类回复。
原后端route.js代码
const express = require('express'); const bodyParser = require ('body-parser'); const mysql =require ('mysql'); const connection = mysql.createPool ({ host :'127.0.0.1', //port :'8012', user :'root', password :'password', database :'test_db' }); const app = express (); //Creating a get route that returns data from users table app.get('/users' , function (req,res) { //Connecting to DB connection.getConnection (function (err, connection) { //Executing query connection.query('SELECT * FROM users', function (error, results, fields){ if (error) throw error; res.send(results) }); }); }); //starting our server app.listen (3000, () =>{ console.log('Go to http://127.0.0.1:3000/users so you can see the data.'); });
原前端LoginScreen代码
import React, { useState } from 'react' import { TouchableOpacity, StyleSheet, View, Image} from 'react-native' import { Text } from 'react-native-paper' import Background from '../components/Background' import Logo from '../components/Logo' import Header from '../components/Header' import Button from '../components/Button' import TextInput from '../components/TextInput' import BackButton from '../components/BackButton' import { theme } from '../core/theme' import { emailValidator } from '../helpers/emailValidator' import { passwordValidator } from '../helpers/passwordValidator' import * as webBrowser from 'expo-web-browser' import * as google from 'expo-auth-session/providers/google' //web: 59045300049-q41hk0a5130f1s5cagsjkqk9jk2gniap.apps.googleusercontent.com //ios: 59045300049-2krv3gobllv0i32vmbl0pkd3vpp45l99.apps.googleusercontent.com //android: 59045300049-t654e5tdf62tf2qfihq47m95ib404vle.apps.googleusercontent.com webBrowser.maybeCompleteAuthSession(); export default function LoginScreen({ navigation }) { const [accessToken, setAccessToken] = React.useState(null); const [user, setUser] = React.useState(null); const [request, response, promptAsync] = google.useIdTokenAuthRequest({ clientId: "59045300049-q41hk0a5130f1s5cagsjkqk9jk2gniap.apps.googleusercontent.com", IosClientId: "59045300049-2krv3gobllv0i32vmbl0pkd3vpp45l99.apps.googleusercontent.com", AndroidClientId: "59045300049-t654e5tdf62tf2qfihq47m95ib404vle.apps.googleusercontent.com" }); React.useEffect(() => { if(response?. type == "success"){ setAccessToken(response.authentication.accessToken); accessToken && fetchUserInfo(); } }, [response, accessToken]); async function fetchUserInfo(){ let response = await fetch("https://www.googleapis.com/userinfo/v2/me", { headers: { authorization: 'Bearer ${accessToken}'} }) const useInfo = await response.json(); setUser(useInfo); } const [email, setEmail] = useState({ value: '', error: '' }) const [password, setPassword] = useState({ value: '', error: '' }) const onLoginPressed = () => { const emailError = emailValidator(email.value) const passwordError = passwordValidator(password.value) if (emailError || passwordError) { setEmail({ ...email, error: emailError }) setPassword({ ...password, error: passwordError }) return } navigation.reset({ index: 0, routes: [{ name: 'Dashboard' }], }) } const ShowUserInfo = () => { if(user) { return( <view style={{flex: 1, alignItems: 'center', justifyContent: 'center'}}> <Text style = {{fontSize: 35, fontWeight: 'bold', marginBottom: 20}}>welcome</Text> <image source = {{uri: user.picture}} style = {{width: 100, height: 100, borderRadius: 50}}/> <Text style= {{fontSize: 20, fontWeight: 'bold'}}>{user.name}</Text> </view> ) } } return ( <Background> <BackButton goBack={navigation.goBack} /> <Logo /> <Header>Welcome back.</Header> <TextInput label="Email" returnKeyType="next" value={email.value} onChangeText={(text) => setEmail({ value: text, error: '' })} error={!!email.error} errorText={email.error} autoCapitalize="none" autoCompleteType="email" textContentType="emailAddress" keyboardType="email-address" /> <TextInput label="Password" returnKeyType="done" value={password.value} onChangeText={(text) => setPassword({ value: text, error: '' })} error={!!password.error} errorText={password.error} secureTextEntry /> <View style={styles.forgotPassword}> <TouchableOpacity onPress={() => navigation.navigate('ResetPasswordScreen')} > <Text style={styles.forgot}>Forgot your password?</Text> </TouchableOpacity> </View> <Button mode="contained" onPress={onLoginPressed}> Login </Button> <View style={styles.container}> {user && <ShowUserInfo />} {user === null && <> <TouchableOpacity disabled={!request} onPress={() => { promptAsync(); }} > <Image source={require("../assets/google-signin.png")} style={{width: 300, height: 40} }/> </TouchableOpacity> </> } </View> <View style={styles.row}> <Text>Don’t have an account? </Text> <TouchableOpacity onPress={() => navigation.replace('RegisterScreen')}> <Text style={styles.link}>Sign up</Text> </TouchableOpacity> </View> </Background> ) } const styles = StyleSheet.create({ forgotPassword: { width: '100%', alignItems: 'flex-end', marginBottom: 24, }, row: { flexDirection: 'row', marginTop: 10, }, forgot: { fontSize: 13, color: theme.colors.secondary, }, link: { fontWeight: 'bold', color: theme.colors.primary, }, })
解决方案
第一步:改造后端接口,添加登录验证逻辑
现有接口仅能获取所有用户数据,既不安全也不符合登录场景,需要新增POST接口专门处理账号密码验证,同时解决跨域、密码加密问题:
- 安装必要依赖:
npm install bcryptjs cors
- 修改后的
route.js代码:
const express = require('express'); const bodyParser = require('body-parser'); const mysql = require('mysql'); const bcrypt = require('bcryptjs'); const cors = require('cors'); const connection = mysql.createPool({ host: '127.0.0.1', user: 'root', password: 'password', database: 'test_db' }); const app = express(); // 启用跨域,允许React Native请求 app.use(cors()); // 解析JSON格式的请求体 app.use(bodyParser.json()); // 新增登录验证接口 app.post('/login', (req, res) => { const { email, password } = req.body; // 参数校验 if (!email || !password) { return res.status(400).json({ success: false, message: '邮箱和密码不能为空' }); } connection.getConnection((err, conn) => { if (err) throw err; // 根据邮箱查询用户 conn.query('SELECT * FROM users WHERE email = ?', [email], (error, results) => { conn.release(); if (error) throw error; // 用户不存在 if (results.length === 0) { return res.status(401).json({ success: false, message: '邮箱或密码错误' }); } const user = results[0]; // 验证密码(数据库需存储bcrypt加密后的密码) bcrypt.compare(password, user.password, (bcryptErr, isMatch) => { if (bcryptErr) throw bcryptErr; if (isMatch) { // 密码匹配,返回用户信息(剔除敏感字段) const { password, ...userData } = user; res.json({ success: true, user: userData }); } else { res.status(401).json({ success: false, message: '邮箱或密码错误' }); } }); }); }); }); // 保留原用户列表接口(仅测试用,生产环境需添加权限校验) app.get('/users', (req, res) => { connection.getConnection((err, connection) => { connection.query('SELECT * FROM users', (error, results) => { if (error) throw error; res.send(results); }); }); }); app.listen(3000, () => { console.log('Server running on http://127.0.0.1:3000'); });
重要提示:数据库
users表的password字段必须存储bcrypt加密后的字符串,禁止明文存储。如果之前是明文数据,需先通过bcrypt加密更新,或在注册接口添加加密逻辑。
第二步:修改前端登录逻辑,调用后端接口
替换原onLoginPressed函数的直接跳转逻辑,改为调用后端登录接口并处理响应:
// 新增导入Alert用于错误提示 import { TouchableOpacity, StyleSheet, View, Image, Alert } from 'react-native' // ... 其他代码保持不变 const onLoginPressed = async () => { const emailError = emailValidator(email.value) const passwordError = passwordValidator(password.value) if (emailError || passwordError) { setEmail({ ...email, error: emailError }) setPassword({ ...password, error: passwordError }) return } try { // 调用后端登录接口(真机测试需替换127.0.0.1为电脑局域网IP) const response = await fetch('http://127.0.0.1:3000/login', { method: 'POST', headers: { 'Content-Type': 'application/json', }, body: JSON.stringify({ email: email.value, password: password.value, }), }) const result = await response.json() if (result.success) { // 登录成功,跳转至Dashboard navigation.reset({ index: 0, routes: [{ name: 'Dashboard' }], }) } else { // 登录失败,提示错误信息 Alert.alert('登录失败', result.message) } } catch (error) { // 网络请求失败处理 Alert.alert('错误', '无法连接到服务器,请检查网络或后端服务状态') console.error(error) } }
真机测试注意:不能使用
127.0.0.1,需替换为开发电脑的局域网IP,同时确保手机与电脑在同一WiFi网络下。
额外注意事项
- 密码安全:全程禁止明文传输密码,必须使用bcrypt等专用密码哈希算法存储密码。
- 错误处理:前端需覆盖网络错误、服务器错误、业务逻辑错误等场景,给用户明确提示。
- 跨域配置:后端必须启用CORS,否则React Native的网络请求会被拦截。
- 权限控制:生产环境中,用户列表接口需添加身份验证,禁止无权限访问。
内容的提问来源于stack exchange,提问作者Dryd22
相关产品推荐
相关产品推荐

