やりたいこと
- ログイン(認証)を突破していないアカウントが、ログイン・アカウント登録画面以外にアクセスできないようにしたい
- 現状だとURL(
/home
)を入力すると認証されていないユーザーでもホーム画面にアクセスできてしまう
- 現状だとURL(
- ログインしている状態のアカウントがログイン・アカウント登録画面にアクセスできないようにしたい
前提
下記の対応についてはすでに完了しているものとします(対応内容の詳細については過去記事をご参照ください)
- Angular CLIによるAngularプロジェクトの作成
- メールアドレスとパスワードを用いたFirebase認証
- ログアウト機能の実装
- ログイン・ホーム画面などの作成
Guardを作成する
Angular CLIでプロジェクトを作成している場合は、下記のコマンドでauthentication.guard.ts
ファイルが作成されます。
$ ng g guard authentication
CLIで作成された初期状態のauthentication.guard.ts
です。
authentication.guard.ts
import { Injectable } from '@angular/core'; import { CanActivate, ActivatedRouteSnapshot, RouterStateSnapshot, UrlTree } from '@angular/router'; import { Observable } from 'rxjs'; @Injectable({ providedIn: 'root' }) export class AuthenticationGuard implements CanActivate { canActivate( next: ActivatedRouteSnapshot, state: RouterStateSnapshot): Observable<boolean | UrlTree> | Promise<boolean | UrlTree> | boolean | UrlTree { return true; } }
canActivate
メソッドでは、Guard
を設定したURLに対してのアクセスを許可する場合はtrue
を、アクセスを許可しない場合はfalse
を返すようにします。
ログインしてないアカウントをホーム画面に入れない
空になっているcanActivate
メソッドに、ログインしていないアカウントをホーム画面に遷移させない処理を書いていきます。
authentication.guard.ts
import { Injectable } from '@angular/core'; import { CanActivate, ActivatedRouteSnapshot, RouterStateSnapshot, UrlTree, Router } from '@angular/router'; import { Observable } from 'rxjs'; import { AngularFireAuth } from '@angular/fire/auth'; import { take, map } from 'rxjs/operators'; /** * ログインしていないアカウントをログイン画面に遷移させる * * @export * @class AuthenticationGuard * @implements {CanActivate} */ @Injectable({ providedIn: 'root' }) export class AuthenticationGuard implements CanActivate { constructor(private afAuth: AngularFireAuth, private router: Router) {} canActivate( next: ActivatedRouteSnapshot, state: RouterStateSnapshot ): | Observable<boolean | UrlTree> | Promise<boolean | UrlTree> | boolean | UrlTree { return this.afAuth.user.pipe( take(1), map(user => { if (user != null) { // ログインしていた場合userにユーザーの情報が入る return true; } else { // ログインしていない場合はログイン画面に遷移する this.router.navigate(['/login']); return false; } }) ); } }
AngularFireAuth.user
はObservable<User|null>
を返すため、user
がnull
ならばログインしていない、user
が設定されていればログインしている、といった判定をすることができます。
作成したAuthenticationGuard
をAppRoutingModule
に設定するとGuard
の実装が完了です。
- app-routing.module.ts
import { NgModule } from '@angular/core'; import { RouterModule, Routes } from '@angular/router'; import { HomeComponent } from './home/home.component'; import { LoginComponent } from './login/login.component'; import { SignUpComponent } from './sign-up/sign-up.component'; import { AuthenticationGuard } from './authentication.guard'; const routes: Routes = [ { path: '', redirectTo: '/login', pathMatch: 'full' }, { path: 'login', component: LoginComponent }, { path: 'home', component: HomeComponent, canActivate: [AuthenticationGuard] // <- add this! }, { path: 'sign-up', component: SignUpComponent } ]; @NgModule({ imports: [RouterModule.forRoot(routes)], exports: [RouterModule] }) export class AppRoutingModule {}
ログイン済みのアカウントをログイン画面に入れない
AuthenticationGuard
でやったことの逆をやります。まず、Angular CLIでGuard
ファイルを作成します。
ng g guard authenticated
作成されたauthenticated.guard.ts
ファイルを使って、ログイン済みのアカウントがログイン・アカウント登録画面に遷移するのを禁止する処理を実装します。
- authenticated.guard.ts
import { Injectable } from '@angular/core'; import { CanActivate, ActivatedRouteSnapshot, RouterStateSnapshot, UrlTree, Router } from '@angular/router'; import { Observable } from 'rxjs'; import { AngularFireAuth } from '@angular/fire/auth'; import { take, map } from 'rxjs/operators'; /** * ログイン済のアカウントをログイン・アカウント登録画面に、 * 遷移させない * * @export * @class AuthenticatedGuard * @implements {CanActivate} */ @Injectable({ providedIn: 'root' }) export class AuthenticatedGuard implements CanActivate { constructor(private afAuth: AngularFireAuth, private router: Router) {} canActivate( next: ActivatedRouteSnapshot, state: RouterStateSnapshot ): | Observable<boolean | UrlTree> | Promise<boolean | UrlTree> | boolean | UrlTree { return this.afAuth.user.pipe( take(1), map(user => { if (user != null) { // ログインしていた場合はホーム画面に遷移する this.router.navigate(['/home']); return false; } else { // ログインしていない場合は遷移を許可する return true; } }) ); } }
今度はuser
がnull
の場合に、Observable型のtrue
を返して画面への遷移を許可しています。反面、user
がnull
でないログイン済のアカウントの遷移は許可せずにホーム画面に遷移させています。
作成したAuthenticatedGuard
をlogin
とsign-up
のルーティングに設定すると、認証済のアカウントによるログイン・アカウント登録画面への遷移を禁止することができます。
- app-routing.module.ts
import { NgModule } from '@angular/core'; import { RouterModule, Routes } from '@angular/router'; import { HomeComponent } from './home/home.component'; import { LoginComponent } from './login/login.component'; import { SignUpComponent } from './sign-up/sign-up.component'; import { AuthenticationGuard } from './authentication.guard'; import { AuthenticatedGuard } from './authenticated.guard'; const routes: Routes = [ { path: '', redirectTo: '/login', pathMatch: 'full' }, { path: 'login', component: LoginComponent, canActivate: [AuthenticatedGuard] // <- add this! }, { path: 'home', component: HomeComponent, canActivate: [AuthenticationGuard] }, { path: 'sign-up', component: SignUpComponent, canActivate: [AuthenticatedGuard] // <- add this! } ]; @NgModule({ imports: [RouterModule.forRoot(routes)], exports: [RouterModule] }) export class AppRoutingModule {}
動作確認
当初の仕様通りにアクセスをブロックできているか確認します。
未ログインのアカウントをホーム画面に遷移させない
ログインしていない状態から、不正にURLを書き換えて、ホーム画面に遷移しようとします。
ホーム画面に遷移せずに、ログイン画面に戻されました。成功です。
ログイン済のアカウントをログイン画面に遷移させない
今度はログイン済のアカウントを使って、ホーム画面からログイン画面への遷移を試みます。
URLをlogin
に書き換えて不正アクセスを試みますが、失敗します。こちらも成功です。
バージョン情報
- Angular v7.2.0
- firebase: v6.3.4
- Angular Material v7.3.7
参考サイト
angularfire/getting-started.md at master · angular/angularfire · GitHub