react-native-firebase 登陸功能集成與實現

如下內容基於 react-native-firebase 版本4.1 javascript

經常使用api:
//匿名登陸:
    firebase.auth().signInAnonymously()
            .then(() => { this.setState({ isAuthenticated: true, }); }); /** * 手機號碼登陸 * @returns {Promise<void>} * @private */ _loginWithPhone = async () => {
        console.log('手機登陸');
        try {
            const ConfirmationResult = await firebase.auth().signInWithPhoneNumber('+86' + this.state.username);
            console.log(ConfirmationResult);

            //如下內容是校驗驗證碼是否正確,123456是咱們默認的驗證碼
            //校驗成功就登陸了,應該寫在兩個方法中依次觸發
            try {
                let user = await ConfirmationResult.confirm('123456');
                console.log(user)
            } catch (e) {
                console.log(e)
            }

        } catch (e) {
            console.log(e)
        }

    };

    /**
     * 郵箱登陸
     * @returns {Promise<void>}
     * @private
     */
    _loginWithEmail = async () => {
        console.log('郵箱登陸');
        try {
            let loginResult = await firebase.auth().signInAndRetrieveDataWithEmailAndPassword(this.state.username, this.state.password);
            console.log(loginResult)
        } catch (e) {
            console.log(e);
            alert('帳戶信息錯誤')
        }
    };
/**
     * 根據輸入內容判斷是郵箱仍是手機號
     * 其實應該分別用兩個不一樣控件實現
     * 由於郵箱是直接輸入帳號和密碼
     * 手機號的話要發送並等待驗證碼
     */
    _login = () => {
        let {username} = this.state;
        let emailReg = /\w+([-+.]\w+)*@\w+([-.]\w+)*\.\w+([-.]\w+)*/;
        let phoneReg = /^[1][0-9]{10}$/;

        console.log(`start login:${this.state.username}, ${this.state.password}`);

        if (emailReg.test(username)) {
            this._loginWithEmail()
        } else if (phoneReg.test(username)) {
            this._loginWithPhone()
        } else {
            alert('請輸入正確的郵箱或電話')
        }
    };    

//查看登陸用戶信息
 console.log(firebase.auth().currentUser)

//退出登陸
firebase.auth().signOut()

//監聽登陸狀態
    this.unsubscribe = firebase.auth().onAuthStateChanged((user) => { if (user) { this.setState({ user: user.toJSON() }); } else { // 用戶已退出,重置狀態 this.setState({ user: null, message: '', codeInput: '', phoneNumber: '+44', confirmResult: null, }); } });

安裝

iOS安裝

首先確保您已遵循。初始設置指南 java

Add the pod

將如下內容添加到您的Podfile:react

pod 'Firebase/Auth'

run pod update。android

若是您打算使用電子郵件登陸連接,那麼您還須要遵循。iOS動態連接安裝指南ios

Android安裝

添加依賴項

將Firebase身份驗證依賴項添加到android/app/build.gradle:web

dependencies {
  // ...
  implementation "com.google.firebase:firebase-auth:15.1.0"
}

若是您打算使用電子郵件登陸連接,則還須要遵循Android的動態連接安裝指南數據庫

安裝RNFirebase身份驗證包

添加RNFirebaseAuthPackage到您的android/app/src/main/java/com/[app name]/MainApplication.java:react-native

// ...
import io.invertase.firebase.RNFirebasePackage;
import io.invertase.firebase.auth.RNFirebaseAuthPackage; // <-- Add this line

public class MainApplication extends Application implements ReactApplication {
    // ...

    @Override
    protected List<ReactPackage> getPackages() {
      return Arrays.<ReactPackage>asList(
          new MainReactPackage(),
          new RNFirebasePackage(),
          new RNFirebaseAuthPackage() // <-- Add this line
      );
    }
  };
  // ...
}

身份驗證 - 入門

不管您是在尋找匿名,電子郵件和密碼仍是社交身份驗證,Firebase均可讓您輕鬆實現身份驗證。api

對於新手來講,雖然API很簡單,但實際上將其應用到應用程序的流程中可能看起來很棘手。如下指南將介紹您可能會遇到的幾種狀況。bash

咱們只會在這裏使用React Native和RNFirebase以免任何混淆,儘管您可能但願將其與Redux等狀態管理工具聯繫起來,由於您的應用程序變得更加複雜。

RNFirebase使用本機Firebase SDK,所以您的身份驗證狀態將保存到設備,這意味着若是您關閉應用程序並從新打開應用程序,則用戶無需從新進行身份驗證。

匿名認證

即便你的應用程序可能沒有任何須要的身份驗證,若是你能以某種方式跟蹤你的用戶,給他們一個惟一的標識符,這不是很好嗎?

這正是匿名身份驗證所作的事情。它爲用戶建立賬戶,而無需進行身份驗證。

確保您已在身份驗證下Anonymous的Firebase控制檯上啓用了登陸!
開啓登陸

匿名登陸方法:

import firebase from 'react-native-firebase';
...
    /**
     * 匿名登陸
     * @private
     */
    _anonymousLogin = () => {
        firebase.auth().signInAnonymously()
            .then(() => {
                this.setState({
                    isAuthenticated: true,
                });
            });
    }
...

render(){
<Text onPress={this._anonymousLogin}>{`匿名登陸:${this.state.isAuthenticated}`}</Text>
    )
}

在顯示應用程序以前匿名驗證用戶身份。咱們將使用auth#signInAnonymously方法來執行此操做::

import React from 'react';
import { View, Text } from 'react-native';
import firebase from 'react-native-firebase';

class App extends React.Component {

  constructor() {
    super();
    this.state = {
      isAuthenticated: false,
    };
  }

  componentDidMount() {
    firebase.auth().signInAnonymously()
      .then(() => {
        this.setState({
          isAuthenticated: true,
        });
      });
  }

  render() {
    // If the user has not authenticated
    if (!this.state.isAuthenticated) {
      return null;
    }

    return (
      <View>
        <Text>Welcome to my awesome app!</Text>
      </View>
    );
  }

}

export default App;

如今應用程序將不會呈現內容,直到auth#signInAnonymously方法已解決。您如今可使用auth#currentUser屬性訪問匿名用戶詳細信息。

<Text onPress={() => {
                        console.log(firebase.auth().currentUser)
                    }}>查看匿名登陸信息</Text>

若是用戶關閉並從新打開應用,Firebase會自動將其從新登陸到已分配的匿名賬戶(若是可用)。例如將登陸方法寫入componentDidMount

必需的電子郵件/密碼驗證

另外一種常見狀況是要求用戶在能夠訪問您的應用以前登陸賬戶。您可能也但願他們也能夠註銷。

幸運的是,經過提供auth#signInWithEmailAndPassword方法,Firebase使這一切變得很是簡單。若是您還須要註冊屏幕,請改用auth#createUserWithEmailAndPassword。
請改用 signInAndRetrieveDataWithEmailAndPassword
確保您已在身份驗證下Email/Password的Firebase控制檯上啓用了登陸!

與匿名身份驗證很是類似,咱們須要使用狀態根據當前的Firebase身份驗證狀態呈現應用。
主要區別在於咱們須要跟蹤用戶當前的身份驗證狀態(用於登陸,建立賬戶或註銷)。這可使用auth#onAuthStateChanged方法完成:

咱們還將跟蹤應用程序卸載時的取消訂閱者功能。
Login.js

/** * 郵箱登陸 * @returns {Promise<void>} * @private */
    _loginWithEmail = async () => {
        try {
            let a = await firebase.auth().signInAndRetrieveDataWithEmailAndPassword('zhuoyuan93@gmail.com', 'password');
            console.log(a)
        } catch (e) {
            console.log(e);
            alert("密碼錯誤")
        }

    }

    render() {
        return (
            <View>
                <Text onPress={this._loginWithEmail}>
                    LOGIN頁面
                </Text>

            </View>
        );
    }

登陸成功以後會自動刷新頁面。
我在方法中直接寫了參數(郵件地址和密碼),實際開發中能夠有郵件和密碼輸入框,而後登陸按鈕調用方法firebase.auth().signInAndRetrieveDataWithEmailAndPassword(email,password)
由於我密碼是不對的,因此會走catch的內容,若是是正確的密碼,則會打印出帶有登陸的user消息的內容並在短暫延遲後刷新頁面。

import React from 'react';
import { View, Text } from 'react-native';
import firebase from 'react-native-firebase';

import Login from './screens/Login';

class App extends React.Component {

  constructor() {
    super();
    this.unsubscriber = null;
    this.state = {
      user: null,
    };
  }

  /** * Listen for any auth state changes and update component state */
  componentDidMount() {
    this.unsubscriber = firebase.auth().onAuthStateChanged((user) => {
      this.setState({ user });
    });
  }

  componentWillUnmount() {
    if (this.unsubscriber) {
      this.unsubscriber();
    }
  }

  render() {
    if (!this.state.user) {
      return <Login />;
    }

    return (
      <View>
        <Text>Welcome to my awesome app {this.state.user.email}!</Text>
      </View>
    );
  }

}

當onAuthStateChanged監聽器返回用戶或者null,咱們能夠直接將其傳遞給咱們的狀態。

發生身份驗證事件時,將使用咱們的用戶(已登陸或建立賬戶)或null(已註銷)更新用戶狀態。若是用戶狀態爲null,則顯示登陸屏幕。

電話驗證

RNFirebase提供了兩種使用電話號碼與用戶簽名的方法; auth#signInWithPhoneNumber和auth#verifyPhoneNumber。二者都提供了不一樣的工做流程,能夠最好地將其與您的應 signInWithPhoneNumber一旦用戶確認了他們的電話號碼,他們將自動簽署用戶,同時verifyPhoneNumber將提供有關當前驗證狀態的反饋,但要求您在確認後手動簽署用戶。

signInWithPhoneNumber

signInWithPhoneNumber 是處理auth流程的更直接的實現,可是它提供了較少的靈活性來處理可能發生的各類狀況。

1.觸發電話驗證

try {
            const ConfirmationResult = await firebase.auth().signInWithPhoneNumber('+86' + this.state.username);
            console.log(ConfirmationResult);
        } catch (e) {
            console.log(e)
        }

返回結果:

2.確認驗證碼

try {
                let user = await ConfirmationResult.confirm('123456');
                console.log(user)
            } catch (e) {
                console.log(e)
            }

驗證成功打印的結果:
這裏寫圖片描述
能夠看到,驗證成功以後會返回user的數據,RNFirebase會保存起來。
以後能夠經過firebase.auth().currentUser拿到user的相關信息

在iOS上,這就是所須要的。

可是,若是Google Play服務能夠自動檢測傳入的消息(而且不顯示它!),Android也會提供「自動驗證」。

3. [Android]處理自動驗證

要處理自動驗證,咱們須要監聽auth#onAuthStateChanged:

this.unsubscriber = firebase.auth().onAuthStateChanged((user) => { // 用戶已經經過驗證並已登陸 this.setState({user}); });

這個是監聽是否登陸的,能夠寫在componentDidMount中,記得卸載。
如下是官方給的完整例子,我加了一些註釋:

import React, { Component } from 'react';
import { View, Button, Text, TextInput, Image } from 'react-native';

import firebase from 'react-native-firebase';

const successImageUri = 'https://cdn.pixabay.com/photo/2015/06/09/16/12/icon-803718_1280.png';

export default class PhoneAuthTest extends Component {
  constructor(props) {
    super(props);
    this.unsubscribe = null;
    this.state = {
      user: null,
      message: '',
      codeInput: '',
      phoneNumber: '+44',
      confirmResult: null,
    };
  }

  componentDidMount() {
  //監聽登陸狀態
    this.unsubscribe = firebase.auth().onAuthStateChanged((user) => { if (user) { this.setState({ user: user.toJSON() }); } else { // 用戶已退出,重置狀態 this.setState({ user: null, message: '', codeInput: '', phoneNumber: '+44', confirmResult: null, }); } }); } componentWillUnmount() { //卸載組件時,卸載掉監聽事件 if (this.unsubscribe) this.unsubscribe(); } //發送驗證碼 signIn = () => {
    const { phoneNumber } = this.state;
    this.setState({ message: 'Sending code ...' });

    firebase.auth().signInWithPhoneNumber(phoneNumber)
      .then(confirmResult => this.setState({ confirmResult, message: 'Code has been sent!' }))
      .catch(error => this.setState({ message: `Sign In With Phone Number Error: ${error.message}` }));
  };

//驗證驗證碼是否正確
  confirmCode = () => {
    const { codeInput, confirmResult } = this.state;

    if (confirmResult && codeInput.length) {
      confirmResult.confirm(codeInput)
        .then((user) => { this.setState({ message: 'Code Confirmed!' }); }) .catch(error => this.setState({ message: `Code Confirm Error: ${error.message}` })); } }; //退出登陸 signOut = () => {
    firebase.auth().signOut();
  }

//輸入手機號的組件
  renderPhoneNumberInput() {
   const { phoneNumber } = this.state;

    return (
      <View style={{ padding: 25 }}>
        <Text>Enter phone number:</Text>
        <TextInput
          autoFocus
          style={{ height: 40, marginTop: 15, marginBottom: 15 }}
          onChangeText={value => this.setState({ phoneNumber: value })}
          placeholder={'Phone number ... '}
          value={phoneNumber}
        />
        <Button title="Sign In" color="green" onPress={this.signIn} />
      </View>
    );
  }

//實時狀態顯示
  renderMessage() {
    const { message } = this.state;

    if (!message.length) return null;

    return (
      <Text style={{ padding: 5, backgroundColor: '#000', color: '#fff' }}>{message}</Text>
    );
  }

//驗證碼組件
  renderVerificationCodeInput() {
    const { codeInput } = this.state;

    return (
      <View style={{ marginTop: 25, padding: 25 }}>
        <Text>Enter verification code below:</Text>
        <TextInput
          autoFocus
          style={{ height: 40, marginTop: 15, marginBottom: 15 }}
          onChangeText={value => this.setState({ codeInput: value })}
          placeholder={'Code ... '}
          value={codeInput}
        />
        <Button title="Confirm Code" color="#841584" onPress={this.confirmCode} />
      </View>
    );
  }

  render() {
    const { user, confirmResult } = this.state;
    return (
      <View style={{ flex: 1 }}>

        {!user && !confirmResult && this.renderPhoneNumberInput()}

        {this.renderMessage()}

        {!user && confirmResult && this.renderVerificationCodeInput()}

        {user && (
          <View
            style={{
              padding: 15,
              justifyContent: 'center',
              alignItems: 'center',
              backgroundColor: '#77dd77',
              flex: 1,
            }}
          >
            <Image source={{ uri: successImageUri }} style={{ width: 100, height: 100, marginBottom: 25 }} />
            <Text style={{ fontSize: 25 }}>Signed In!</Text>
            <Text>{JSON.stringify(user)}</Text>
            <Button title="Sign Out" color="red" onPress={this.signOut} />
          </View>
        )}
      </View>
    );
  }
}

verifyPhoneNumber

此實現使您能夠徹底控制任一平臺上的電話號碼驗證流程以及適合任何UI流程的靈活API,若是您使用了諸如存儲,上傳任務/數據庫等內容,則應該熟悉使用。若是您想要將電話憑證與現有賬戶相關聯,而不是僅僅簽署用戶,則特別有用。

firebase.auth()
  .verifyPhoneNumber(phoneNumber)
  .on('state_changed', (phoneAuthSnapshot) => { //你如何處理這些狀態事件徹底取決於你的ui流程以及你是否須要同時支持ios和android。 //簡而言之:並不是全部人都須要處理 - 這徹底取決於你,你的ui和支持的平臺。 // E.g 你能夠只在這裏處理特定於Android的事件,而後讓其他部分回到optionalErrorCb或optionalCompleteCb函數 switch (phoneAuthSnapshot.state) { // ------------------------ // IOS AND ANDROID EVENTS // ------------------------ case firebase.auth.PhoneAuthState.CODE_SENT: // or 'sent' console.log('code sent'); // on ios this is the final phone auth state event you'd receive // so you'd then ask for user input of the code and build a credential from it // as demonstrated in the `signInWithPhoneNumber` example above break; case firebase.auth.PhoneAuthState.ERROR: // or 'error' console.log('verification error'); console.log(phoneAuthSnapshot.error); break; // --------------------- // ANDROID ONLY EVENTS // --------------------- case firebase.auth.PhoneAuthState.AUTO_VERIFY_TIMEOUT: // or 'timeout' console.log('auto verify on android timed out'); // proceed with your manual code input flow, same as you would do in // CODE_SENT if you were on IOS break; case firebase.auth.PhoneAuthState.AUTO_VERIFIED: // or 'verified' // auto verified means the code has also been automatically confirmed as correct/received // phoneAuthSnapshot.code will contain the auto verified sms code - no need to ask the user for input. console.log('auto verified on android'); console.log(phoneAuthSnapshot); // Example usage if handling here and not in optionalCompleteCb: // const { verificationId, code } = phoneAuthSnapshot; // const credential = firebase.auth.PhoneAuthProvider.credential(verificationId, code); // Do something with your new credential, e.g.: // firebase.auth().signInWithCredential(credential); // firebase.auth().currentUser.linkWithCredential(credential); // etc ... break; } }, (error) => { // optionalErrorCb would be same logic as the ERROR case above, if you've already handed // the ERROR case in the above observer then there's no need to handle it here console.log(error); // verificationId is attached to error if required console.log(error.verificationId); }, (phoneAuthSnapshot) => { // optionalCompleteCb would be same logic as the AUTO_VERIFIED/CODE_SENT switch cases above // depending on the platform. If you've already handled those cases in the observer then // there's absolutely no need to handle it here. // Platform specific logic: // - if this is on IOS then phoneAuthSnapshot.code will always be null // - if ANDROID auto verified the sms code then phoneAuthSnapshot.code will contain the verified sms code // and there'd be no need to ask for user input of the code - proceed to credential creating logic // - if ANDROID auto verify timed out then phoneAuthSnapshot.code would be null, just like ios, you'd // continue with user input logic. console.log(phoneAuthSnapshot); }); // optionally also supports .then & .catch instead of optionalErrorCb & // optionalCompleteCb (with the same resulting args)