You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

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接口专门处理账号密码验证,同时解决跨域、密码加密问题:

  1. 安装必要依赖:
npm install bcryptjs cors
  1. 修改后的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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.26 22:47:00