2018年10月23日 星期二

【Ionic 4】防止任意存取頁面—Angular路徑保護機制Route Guards

Firebase提供email登入與註冊功能,前文(【Ionic 4】新頁面瀏覽機制:Angular Router)就註冊做介紹,一旦註冊成功,後續便可直接登入,進入個人頁面,例如dashboard等。然而,在Angular Router機制下,Ionic apps各個頁面皆有直接存取路徑,如'/login', '/dashboard';只要輸入路徑便可前往該頁面。因此,必須有保護路徑的情境設定,例如'/dashboard'要在登入之後才允許進入。Angular Router提供了route guards,如CanActivate等界面(interface),透過實做(implement)這些界面,便可達到保護路徑的目的。底下以Firebase登入/註冊,以及個人dashboard三個頁面為例,說明Route Guards的使用方式。

本文使用套件@angular 6.1.1,@angular/fire 5.0.2與firebase 5.5.4。@ionic-angular版本則為4.0.0-beta.12。完整專案詳見GitHub專案


建立Ionic 4專案
使用Ionic CLI 4版新加入的選項--type=angular,建立使用Angular Routing機制(可參考前文【Ionic 4】新頁面瀏覽機制:Angular Router) 的專案,並新增所需頁面: 
ionic start firebaseAuthExample blank --type=angular
cd firebaseAuthExample
ionic g page login
ionic g page register
ionic g service services/auth
ionic g guard services/auth
隨後修正專案內容,並將src/app/app-routing.module.ts檔裡Routes變數之'redirectTo'選項,導向LoginPage。
const routes: Routes = [
  { path: '', redirectTo: 'login', pathMatch: 'full' },  
  { path: 'home',loadChildren: './home/home.module#HomePageModule'},
  { path: 'login', loadChildren: './login/login.module#LoginPageModule' },
  { path: 'register', loadChildren: './register/register.module#RegisterPageModule' },
  { path: '**', redirectTo: 'login'}
];
此處'home'路徑需要保護,實做route guards(即前述ionic g guard services/auth指令) 便是要完成保護的目的。
步驟0:Firebase相關設定
Firebase專案的準備工作、修改src/environments/environment.ts以及app.module.ts等,可參考前文【Ionic 4】會員Email註冊-以Firebase為雲端平台(含FormBuilder表單與輸入驗證)的說明。

步驟1:編輯auth.guard.ts保護頁面
建立專案最後一步ionic g guard services/auth產生了services/auth.guard.ts,預設內容如下:

實做Route Guard界面CanActivate(第7行),用來決定某個路徑是否可以瀏覽;因此,canActivate()方法內(第9-14行),如果該路徑可以瀏覽,則回傳true;如不能瀏覽,則回傳false。實做完畢的auth guard,便可在app-routing.module.ts裡,用來保護路徑,例如保護'/home':
import { AuthGuard } from './services/auth.guard';
//...
const routes: Routes = [
  //...,
  {
    path: 'home',
    loadChildren: './home/home.module#HomePageModule',
    canActivate: [AuthGuard]
  },
  //..
]
留意canActivate屬性值是陣列,可指定多個guards。
AuthGuard完整程式碼如下。裡面用到使用firebase auth api的onAuthStateChanged(),觀察使用者登入的狀態變化:

  • 第6-7行:第6行先引入Firebase的核心功能('firebase/app'),並以firebase為namespace。第7行則引入auth功能。如此便可使用onAuthStateChanged(),觀察使用者登入狀態的改變(第18行)。
  • 第17-26行:自行建立promise,當有使用者登入時,回傳true;若無,則頁面導向至'/login'。

步驟2:login頁面與auth登入服務
login頁面和前文【Ionic 4】會員Email註冊-以Firebase為雲端平台(含FormBuilder表單與輸入驗證)一樣,使用Reactive Forms設計表單、提供欄位輸入驗證。因此,同樣要先在login.module.ts檔引入ReactiveFormsModule
import { ReactiveFormsModule } from '@angular/forms';
//... @NgModule({ imports: [ //..., ReactiveFormsModule, ], //... }) export class LoginPageModule {}

另外,表單送出時,則會呼叫firebase auth api的signInWithEmailAndPassword(),進行登入。故src/app/services/auth.service.ts要加入signIn()程式碼:
signIn(user){
    return this.afAuth.auth.signInWithEmailAndPassword(user.email, user.password)
    .then(credential=>{
      console.log('登入成功');
    })
    .catch(error=> {
      console.log('登入失敗',error);
      this.router.navigate(['/login']);
    });
  }
若登入失敗,將回到login頁面。登入成功則不須做任何動作,因為在constructor()處,已經建立了Observable變數,currentUser,該變數透過FirebaseAuth的authState變數,觀察登入使用者的profile('users/$uid')資料變化:

  • 第9-12行:訂閱authState資料流,再搭配switchMap與valueChanged()便可取得目前登入的使用者資料(switchMap確保換人登入後,資料流會置換掉舊的user profile,只保留新登入者;valueChanged()則是確保user profile為最新資料)。

完整專案詳見GitHub專案

沒有留言:

張貼留言