Android Auth(2-2)Google 整合-完成認證

這一篇是根據上一篇的後續,如果還沒看過上一篇,請先參閱:

Android Auth(2-1)Google 整合認證-設定環境/UI

這樣我們就可以開始寫程式了,我們要實作 Firebase 整合 Google 認證登入,需要了解三個物件,兩個與 Google 帳號有關,另一個和 Firebase Aentication 相關,分別介紹如下:
SignInButton:這是用來啟動 Google 登入頁的 View,這在前一篇設定 UI 時,己經介紹過了
GoogleApiClient :這是我們用來和 Google 登入溝通協調的工具,包括UI 的產生等
FirebaseAuth:這是我們用來和 Firebase 登入溝通協調的工具

而登入的主流程如下:

  1. 建立以上三物件的實體
  2. 由 SignInButton 啟動用 GoogleApiClient 所產出的登入 Intent 並等待回傳
  3. GoogleApiClient 回傳登入成功與否,若成功就使用該帳號登入 Firebase
  4. 若成功就顯示相關登入者名稱資訊

另外,還有一個登出的方法需要實作

第一步,來產出物件的實體,SignInButton 我們在介面檔己經作出來了,所以只需要連繫到就可以了,FirebaseAuth 的實體也很簡單,只要用 .getInstance() 方法就可以取得了, GoogleApiClient 就麻煩一點,要先建立一個設定用的 GoogleSignInOptions 的實體,再用Builder 方法來產生,在產生時,還要設定連線失敗回報的機制,程式碼如下:

signInButton = (SignInButton)findViewById(R.id.singinButton);

// 設定 FirebaseAuth 介面
mAuth = FirebaseAuth.getInstance();

// 設定 Google 登入 Client
GoogleSignInOptions gso = new GoogleSignInOptions.Builder(GoogleSignInOptions.DEFAULT_SIGN_IN)
        .requestIdToken(getString(R.string.default_web_client_id))
        .requestEmail()
        .build();
mGoogleApiClient = new GoogleApiClient.Builder(getApplicationContext())
        .enableAutoManage(this, new GoogleApiClient.OnConnectionFailedListener() {
            @Override
            public void onConnectionFailed(@NonNull ConnectionResult connectionResult) {
                Toast.makeText(MainActivity.this,"Google 連線異常",Toast.LENGTH_LONG).show();
            }
        })
        .addApi(Auth.GOOGLE_SIGN_IN_API,gso)
        .build();

這些程式都是寫在 OnCreate() 方法內。

接下來要把 signInButton 按下時,啟動登入頁面,同樣的寫在 OnCreate() 內,程式碼如下

signInButton.setOnClickListener(new View.OnClickListener(){
    @Override
    public void onClick(View view){
        Intent signInIntent = Auth.GoogleSignInApi.getSignInIntent(mGoogleApiClient);
        startActivityForResult(signInIntent, RC_SIGN_IN);
    }
});

 

\這兒,我用了一個常數 RC_SIGN_IN 做為回傳判別,需要在 class 最前面,設定這個

private  static final  int RC_SIGN_IN = 1;

 

然後,我們就要寫回傳時要做的事了,若成功,就試登入 Firebase,仍是成功就顯示出使者的 display name,在程式中新增兩個方法:

 

@Override
public void onActivityResult(int requestCode, int resultCode, Intent data) {
    super.onActivityResult(requestCode, resultCode, data);
    if (requestCode == RC_SIGN_IN){
        GoogleSignInResult result = Auth.GoogleSignInApi.getSignInResultFromIntent(data);
        if (result.isSuccess()){
            GoogleSignInAccount account = result.getSignInAccount();
            //取得使用者並試登入
            firebaseAuthWithGoogle(account);
        }
    }
}

//登入 Firebase
private  void firebaseAuthWithGoogle(final GoogleSignInAccount account){
    AuthCredential credential = GoogleAuthProvider.getCredential(account.getIdToken(),null);
    mAuth.signInWithCredential(credential)
            .addOnCompleteListener(this, new OnCompleteListener<AuthResult>() {
                @Override
                public void onComplete(@NonNull Task<AuthResult> task) {
                    if (!task.isSuccessful()) {
                        Toast.makeText(MainActivity.this, "Failed", Toast.LENGTH_LONG).show();
                    }else {
                        Toast.makeText(MainActivity.this, "SingIn name:"+account.getDisplayName(), Toast.LENGTH_LONG).show();
                    }
                }

            });
}

 

最後是登出的方法

public void firebaseSingOut(View view){
    // Firebase 登出
    mAuth.signOut();
    
    // Google 登出
    GoogleSignInOptions gso = new GoogleSignInOptions.Builder(GoogleSignInOptions.DEFAULT_SIGN_IN)
            .requestIdToken(getString(R.string.default_web_client_id))
            .requestEmail()
            .build();
    GoogleSignIn.getClient(this, gso).signOut().addOnCompleteListener(new OnCompleteListener<Void>() {
        @Override
        public void onComplete(@NonNull Task<Void> task) {
            Toast.makeText(MainActivity.this, "SingOut", Toast.LENGTH_LONG).show();
        }
    });
}

這樣就完成了我們的程式,執行結果如下

f206f207.PNG

也可以在 Firebase 的控制台中,看到我們登入的帳號:

f208

如此,我們就完成了帳號的登入了。

最後,附上完整的 MainActivity.java

public class MainActivity extends AppCompatActivity {
    private FirebaseAnalytics mFirebaseAnalytics;

    private SignInButton signInButton;
    private Button singOutButton;

    private  static final  int RC_SIGN_IN = 1;

    private GoogleApiClient mGoogleApiClient;
    private FirebaseAuth mAuth;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        // Obtain the FirebaseAnalytics instance.
        mFirebaseAnalytics = FirebaseAnalytics.getInstance(this);
        singOutButton = (Button)findViewById(R.id.singoutButton);


        signInButton = (SignInButton)findViewById(R.id.singinButton);

        // 設定 FirebaseAuth 介面
        mAuth = FirebaseAuth.getInstance();

        // 設定 Google 登入 Client
        GoogleSignInOptions gso = new GoogleSignInOptions.Builder(GoogleSignInOptions.DEFAULT_SIGN_IN)
                .requestIdToken(getString(R.string.default_web_client_id))
                .requestEmail()
                .build();
        mGoogleApiClient = new GoogleApiClient.Builder(getApplicationContext())
                .enableAutoManage(this, new GoogleApiClient.OnConnectionFailedListener() {
                    @Override
                    public void onConnectionFailed(@NonNull ConnectionResult connectionResult) {
                        Toast.makeText(MainActivity.this,"Google 連線異常",Toast.LENGTH_LONG).show();
                    }
                })
                .addApi(Auth.GOOGLE_SIGN_IN_API,gso)
                .build();
        signInButton.setOnClickListener(new View.OnClickListener(){
            @Override
            public void onClick(View view){
                Intent signInIntent = Auth.GoogleSignInApi.getSignInIntent(mGoogleApiClient);
                startActivityForResult(signInIntent, RC_SIGN_IN);
            }
        });

    }

    @Override
    public void onActivityResult(int requestCode, int resultCode, Intent data) {
        super.onActivityResult(requestCode, resultCode, data);
        if (requestCode == RC_SIGN_IN){
            GoogleSignInResult result = Auth.GoogleSignInApi.getSignInResultFromIntent(data);
            if (result.isSuccess()){
                GoogleSignInAccount account = result.getSignInAccount();
                //取得使用者並試登入
                firebaseAuthWithGoogle(account);
            }
        }
    }

    //登入 Firebase
    private  void firebaseAuthWithGoogle(final GoogleSignInAccount account){
        AuthCredential credential = GoogleAuthProvider.getCredential(account.getIdToken(),null);
        mAuth.signInWithCredential(credential)
                .addOnCompleteListener(this, new OnCompleteListener<AuthResult>() {
                    @Override
                    public void onComplete(@NonNull Task<AuthResult> task) {
                        if (!task.isSuccessful()) {
                            Toast.makeText(MainActivity.this, "Failed", Toast.LENGTH_LONG).show();
                        }else {
                            Toast.makeText(MainActivity.this, "SingIn name:"+account.getDisplayName(), Toast.LENGTH_LONG).show();
                        }
                    }

                });
    }

    public void firebaseSingOut(View view){
        // Firebase 登出
        mAuth.signOut();

        // Google 登出
        GoogleSignInOptions gso = new GoogleSignInOptions.Builder(GoogleSignInOptions.DEFAULT_SIGN_IN)
                .requestIdToken(getString(R.string.default_web_client_id))
                .requestEmail()
                .build();
        GoogleSignIn.getClient(this, gso).signOut().addOnCompleteListener(new OnCompleteListener<Void>() {
            @Override
            public void onComplete(@NonNull Task<Void> task) {
                Toast.makeText(MainActivity.this, "SingOut", Toast.LENGTH_LONG).show();
            }
        });
    }
}

Android Auth(2-1)Google 整合-設定環境/UI

要先建立己整合好 SDK 的環境,若你還沒有引入 SDK ,請先看

Android 專案引入 SDK

要使用匿名登入,第一件事要到 Firebase 控制台,開啟該項設定,設定方式很簡單,到控制台,找到我們要用的專案後,按下 Authentication  後選擇登入方式,最後開啟 Google 整合登入後,按下儲存就可以了:

螢幕快照 2018-01-31 上午2.18.18

接下來是要設定程式庫了,我們要在 APP 級的 build.gradle 檔案中,加入我們要用的部份,其實只有一行,在該檔案的 dependencies 段,加上

compile 'com.google.firebase:firebase-auth:11.8.0'
compile 'com.google.android.gms:play-services-auth:11.8.0'

f204.PNG

然後按下右上角的 Sync Now 就可以了。要注意的是,也許用的版本號碼和我用的不一樣,那個版本號碼與 com.google.firebase:firebase-core 有相依性,所以必時,要調整版號,如果一切 OK 的話,那我們就可以開始寫 Code 了。

首先,我們看一下畫面的設計:

f205.PNG

這上面有兩個按鈕,一個用來整合 Google 登入,另一個用來登出,而登入登出後,成功失敗的狀態都用 Toast 顯示出來,我們先看一下 activity_main.xml 檔,重點只有這兩個鈕的部份:

<com.google.android.gms.common.SignInButton
    android:id="@+id/singinButton"
    android:layout_width="200sp"
    android:layout_height="44sp" />
<Button
    android:id="@+id/singoutButton"
    android:layout_width="wrap_content"
    android:layout_height="wrap_content"
    android:text="Sing Out"
    android:onClick="firebaseSingOut"
    app:layout_constraintTop_toBottomOf="@+id/singinButton" />

所以上方是一個 Google SDK 提供的 SignInButton ,用來啟動Google 登入頁面,它會自己因狀況來調介面包括語言等,我們只需要設好大小位置就好了。而下方是一個常見的 Android Button 而我們把它的 onClick 設到 “firebaseSingOut" 以便登出。

這樣,我們要準備的一切就好了,下一篇就是實作程式碼了。

Android Auth(1)匿名登入

若你尚未在引入 SDK 就要先看這一篇

在 Android 專案引入 SDK

有了建好的環境完成引入 Core SDK 之後,就要開始我們的工作了。我們的重點是要 做一個 APP 並使用匿名登入。匿名登入有許多用途,最重要的就是安全性問題,雖是匿名,我們還是可以知道若個APP 登入了,而不是駭客登入,別的服務存取時,也可以有依據。

要使用匿名登入,第一件事要到 Firebase 控制台,開啟該項設定,設定方式很簡單,到控制台,找到我們要用的專案後,按下 Authentication  後選擇登入方式,最後按下匿登入啟用後儲存就可以了。

螢幕快照 2018-01-29 上午12.21.37

接下來是要設定程式庫了,我們要在 APP 級的 build.gradle 檔案中,加入我們要用的部份,其實只有一行,在該檔案的 dependencies 段,加上【 compile ‘com.google.firebase:firebase-auth:11.8.0’】,然後按下右上角的 Sync Now 就可以了。要注意的是,也許用的版本號碼和我用的不一樣,那個版本號碼與 com.google.firebase:firebase-core 有相依性,所以必時,要調整版號,如果一切 OK 的話,那我們就可以開始寫程式了。
f201.PNG

程式的部份,我們暫時不做任可介面,只在開啟時,試著使用匿名登入,不管成功失敗,都回傳訊息。要做到這一件事,就要使用 FirebaseAuth 物件,該物件是我們和 Firebase 認證所需要的介面管道,登入登出和了解登入狀態都靠這個物件。而要取得該物件的實體,就要用 FirebaseAuth.getInstance() 方法。我們取得實體後再使用  .signInAnonymously() 就可以匿名登入了。而這些都寫在 MainActivity 的 onCreate 方法就可以了。先看一下程式碼:

public class MainActivity extends AppCompatActivity {
    private FirebaseAnalytics mFirebaseAnalytics;
    private FirebaseAuth mAuth;
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);

        mAuth = FirebaseAuth.getInstance();
        mAuth.signInAnonymously().addOnCompleteListener(new OnCompleteListener<AuthResult>() {
            @Override
            public void onComplete(@NonNull Task<AuthResult> task) {
                if(task.isSuccessful()){
                    Toast.makeText(MainActivity.this,"匿名登入成功 uid:\n" + mAuth.getCurrentUser().getUid(),Toast.LENGTH_LONG).show();
                }else{
                    Toast.makeText(MainActivity.this,"匿名登入失敗",Toast.LENGTH_LONG).show();
                }
            }
        });
    }
}

在上面的程式中,我們把 FirebaseAuth 存在 mAuth 這個變數中,然後使用這個實體執行 ..signInAnonymously() 方法,最後在完成時,依據成功或失敗的狀態,把訊息使用 Toast 顯示在畫面上,若成功,就顯示使用者的 uid,否則,就顯示失敗。如此,我們就完成了匿名登入的動作。

下面的執行的畫面:
Screenshot_1517577633

然後,我們在控制台的使用者清單中,也會找到相同的 UID 的匿名使用者。

f202.PNG

Android 專案引入 Firebase SDK

相對於 iOS,  Android 由 Google 自家開發工具整合, 所以整合起來非常容易的多, 不過由於 Android 系統版本比較破裂, 所以要先檢查自己是否能用.

在 Android 使用 Firebase SDK 的最低條件有以下幾個

  1. 只支援 SDK Level 9 (Android 2.3) 之後的程式
  2. 至少需要 Google Play Service 9.0 之後的版本,  沒有有 Google Play Service 的手機是不支援的
  3. 使用 Android Studio 2.2 或更新的版本

至於其他的專案, 如 Eclipse,  Xamarin, unity, C++……… 等等許多的 Android 開發方式, Google 並不一定有官方支援,  但經過各平台各自的努力,  可以部份支援了, 但本文討論的重點在原生的開發方式,未來有機會再討論其他的整合方式。

第一個動作,要在 Firebase 平建立 APP , 這個動作和 iOS 是相同的

準備工作:. 登入 Firebase 網站,進入控制台

Firebase 網站位於:

https://firebase.google.com

要使用 Firebase 首先要有一個 Google 帳號,也就是 G+,Gmail 等系統用的帳號,之後就用這個帳號來管理你的 Firebase 應用程式,由於這個帳別必需較嚴謹,所以需要多重驗證,所以你需要準備你的手機,準備收一下簡訊,不過這對大多數人來說問題也不大.

import2.png

成功登入後進入右上方的控制台,或直接到

https://console.firebase.google.com

進入控制台,準備下一個動作

import5.png

  1. 建立你的 Firebase APP

其實這部份要建立兩個東西,第一個是 Firebase 專案。結構上,我們要提供給 iOS, Android , web 等不同平台相同的一份資料,包括登入驗證,推播資料庫等,但各平台的建立方法,需求特性不同所所以首先要建立 Firebase 專案,再於該專案下面,再建站立APP,提供給不同的平台,或同一平台不同的版本,狀態(開發,測試,上架等)。

要建立 Firebase 專案,這個超簡單的,只要 APP 名稱(給自己看的)和國家就可以了,為什麼F002需要國家呢,簡單的講是要收錢的,Firebase 要收費?是的,它是要收費的,Google 慈善機構,不過他也是有佛心來的,不會和窮人收錢,你的APP要長大,才有付費的資格,在免費的狀況下,己經可以做很多事了,若搞到要付錢,那表示你的APP己經小有成就了,若要收到 25$ 以上方案,你就要認真思考你的商業模式了。

建立好 Filebase專案,就要建立APP了,在控制台上,有個明顯的按鈕建立一個APP,

 

f003.PNG

按下去就好了

在按下去的同時,要準備好 你的 Package Name

F001.PNG
在這個畫面中,  要輸入三個項目,  App 的套件名稱,  應用程式暱稱, 與簽署憑證。
App套件名稱就是你的應用程式名稱,  如果忘記了可以到 專案的 build.gradle 去查詢
f004

應用程式暱稱是給自己看的, 輸入自己記得住的名字就好, 不輸入也可以,  不是很重要。第三個簽署憑證是許多服務會用到的,  如動態連結或電話認證等,設定也不是很麻煩,我們可以就一並處理一下。要取得憑證,會用到一個 java 工具叫 keytool,因為你己經安裝好 Android Studio 所以多半己經有這支程式了,只是 macOS/Linux 和 Windows 的狀況不完全一樣

macOS / Linux:

打開命令列,keytool 應該是己經可以使用了,只要打開終端機(如果你不知道如何打開終端機,在 Mac 畫面右上角的放大鏡 Spot Light了 輸入終端機就可以找到了),輸入

keytool -exportcert -list -v -alias androiddebugkey -keystore ~/.android/debug.keystore

不用輸入密碼,按下 Enter 就可以找到 key 了,我們要用的是 SHA-1 的 Key,複製過去就行了

F009

Windows:

Windows 比較麻煩一點是 keytool 不一定在執行路徑上, 所以先要找到它, 就要用到命令列指令,先下 cd \ 到根目錄,再下  dir keytool.exe /s 就能找到:

f010

通常會找到很多 keytool.exe,那是因為若有不同版本的 java, 包括 Open JDK,  64/32.... 位元的版本,都會有一個版本,實際上任可一個版本都可以用。以上例來說,就要到 cd "Program Files\Android\Android Studio\jre\bin" 換目錄到執行檔所在位置, 再下指令:

keytool -exportcert -list -v -alias androiddebugkey -keystore %USERPROFILE%\.android\debug.keystore

就可以取得 key 了

f011.PNG

接下來的動作就比較簡單了,按下下一步
f005

你可以取得一f006個 google-service.json 檔案,畫面會指示下載並複製到專案,  但目前可以先不急著這樣作,只要按繼續就可以了。

最後會有修改 Gradle 的指示,也不要照著作,先按下完成就好了。

為什麼不要按指示做呢?因為實際上, 在新版的 Android Studio (2.3 之後) 有更好的做法。

 

 

 

首先打開你的 Android Studio,建立或打開對應的 Package APP 後,在目錄選單下,選擇 Tools->Firebase 就會出現一個 Assistant  用來連接 Firebase

F012

這是全自動化的整合程式,雖說資深工程師偏好自己設定,但自動設定的好處是較為不會出錯,在深入了解之前,可以先讓自動化的機器幫我們做第一次的工作,減少不必要的困擾,才是上策。
在這個 Assitant 中,並沒有直接連接的選項,但會例出許多服務,建議是先按下 Analytics 因為不管需不需要,這個是使用 Firebase 必要的選項,按下去會出現另一個畫面
f013

這時按下 Connect to Firebase,請輸入和登入 Firebase 相同的Google Account,若你己經用其他帳號登入,需新增該帳號。

接下來,如果 Package Name , SDK 版本都正確的話,按下數個下一步或確認就完成了。

再來要在 MainActivity 中加一個屬性

 

private FirebaseAnalytics mFirebaseAnalytics;

 
並在 onCreate() 中取得 Firebase 服務實體的初始化,就完成了

// Obtain the FirebaseAnalytics instance.
mFirebaseAnalytics = FirebaseAnalytics.getInstance(this);

最後一個動作是執行該程式去確認,可以實用實體機或 VM 不過一定要支援 Google Play Service 才會完全正常反應。為了確認程式有正常運作,正確執行後,需要執行機在網路連線的狀況下按下 Home 鍵,系統就會把 Analytics 的資料回傳,不過真正回傳進入資料庫,整理出來,也是一個或數小時之後的事,但若你由 Android Studio 按下停止執行的話,資料會在數小時之後下次執行才會回傳,所以一定要按下 Home 鍵。

最後,你若在Firebase 的 Analytics 報表中看到了數字,就表示 SDK 正確運作無誤了。

下圖表示有一台機器上線了…..
f014