Google 的第三方登入 (web 前端實作)
概念介紹
要實現第三方登入,有兩種方式
做在前端 ← 這一篇要講的是這個
做在後端
前端的第三方登入
兩者之間的差別在於,前端實作,不需要 redirect url 後端實作的話,需要。(慎選套件)
這一篇,介紹的是做在前端。
這次是要介紹如何妥善安排程式碼在 Vue 裡面。
設定
先到 Google 設定第三方登入的憑證
到這個頁面
如果畫面和我不一樣的話,請先開一個專案
我們要的功能在「憑證」裡面
在憑證裡面,建立一個「OAuth 用戶端 ID」允許第三方登入
點擊 OAuth 用戶端 ID 之後
因為要要做的是 web 所以選「網頁應用程式」
設定 URI 可以用 ngrok 先用一個臨時的來測試。(在此就用 http://127.0.0.1:8080
做代表)
好了就按下「建立」
成功的時候,Google 就會提供用戶端 ID (clientId) 和用戶端密碼,我們只要 clientId
按下確定,就可以來實作程式囉
實作
官方網站
public/index.html
需要載入 Google 的 SDK
<script src="https://apis.google.com/js/platform.js" async defer></script>
Google initial
在頁面初始的過程,需要在 gapi.load 時初始化或自動登入
example code
var startApp = function ( ) { gapi.load('auth2' , function ( ) { auth2 = gapi.auth2.init({ client_id: 'YOUR_CLIENT_ID.apps.googleusercontent.com' , cookiepolicy: 'single_host_origin' , }); attachSignin(document .getElementById('customBtn' )); }); };
在 vue 上實作,必須將這一段放在 main.js 確保不管已登入,還是未登入,在每一次開啟頁面,都會正確初始化
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 import Vue from 'vue' ;import App from './App' ;import router from './router' ;Vue.config.productionTip = false ; new Vue({ created ( ) { window .gapi.load('auth2' , () => { window .gapi.auth2.init({ client_id: 'YOUR_CLIENT_ID.apps.googleusercontent.com' , }); }); }, router, render: h => h(App) }).$mount('#app' );
可以使用 gapi.signin2.render
自訂義繪製時機點
example code
gapi.signin2.render('my-signin2' , { 'scope' : 'profile email' , 'width' : 240 , 'height' : 50 , 'longtitle' : true , 'theme' : 'dark' , 'onsuccess' : onSuccess, 'onfailure' : onFailure });
src/components/googleSigninButton.vue
直接將 google sign-in button 做成一個按鈕。
並且在 mounted 時繪制
Note
用 <div class="g-signin2" data-onsuccess="onSignIn"></div>
這種內建的方式繪製,它只會在網頁初始時繪製按鈕。
但是登出時會出問題:
按下登出後,會跳轉到登入頁,照理需要繪製登入按鈕
因為這並不是在初始過程,所以不會繪製。
登入 method 的綁定方式,要配合 Google API 的做法
在此,目的是為了傳給後端 id_token
所以輸出只 emit 出 id_token 而已。
這部份可以依需求修改。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 <template> <div> <div id="google-sign-in-button" ></div> </div> </template> <script> export default { name: 'GoogleSignInButton' , props: { width: { type: String , default : null }, height: { type: String , default : null } }, mounted ( ) { window .gapi.signin2.render('google-sign-in-button' , { scope: 'profile email' , width: this .width, height: this .height, longtitle: true , theme: 'light' , onsuccess: this .signIn, onfailure: () => {} }); }, methods: { signIn (googleUser ) { const id_token = googleUser.getAuthResponse().id_token; this .$emit('sign-in' , id_token); } } }; </script>
使用 GoogleSignInButton component
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 <template> <div> <GoogleSignInButton @sign-in ="oAuthSignIn('google', $event)" ></GoogleSignInButton> </div> </template> <script> import GoogleSignInButton from '@/components/GoogleSignInButton' ;export default { name: 'AuthSignin' , components: { GoogleSignInButton }, methods: { async oAuthSignIn (provider, id_token ) { try { await this .$store.dispatch('oAuthLogin' , { provider, id_token }); this .$emit('submit' ); } catch (error) { console .error(error); } } } }; </script>
登出
若你想要在登出時,同時斷開使用者給予的授權,就可以增加這一段。
example code
<a href="#" onclick="signOut();" >Sign out</a> <script> function signOut ( ) { var auth2 = gapi.auth2.getAuthInstance(); auth2.signOut().then(function ( ) { console .log('User signed out.' ); }); } </script>
src/views/navigation.vue
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 <template> <div class ="shadow" > <botton @click="Logout" >登出</botton> </div> </template> <script> export default { name: 'Navigation' , methods: { async Logout ( ) { try { const isLogout = window .confirm('確定要登出' ); if (isLogout) { const auth2 = window .gapi.auth2.getAuthInstance(); auth2.signOut().then(() => { console .log('Google User signed out.' ); }); window .alert('已登出' ); this .$router.push({ name : 'Signin' }); } } catch (error) { if (error instanceof Error ) { window .alert(error.messages); } else { window .alert(error.detail); } } } } }; </script> <style lang="scss" scoped> </style>