iT邦幫忙

2018 iT 邦幫忙鐵人賽
DAY 20
0
Modern Web

ngrx/store 4 學習筆記系列 第 20 篇

[ngrx/store-20] Angular 網站實例 - 記得我篇

  • 分享至 

  • xImage
  •  

Angular 網站實例 - 記得我篇

今天開始 今天完成

上次我們已經成功連上了後端,但是只要使用者ㄧ重新刷瀏覽器,又會回到未登入狀態,今天我們就來讓前端記住登入狀態。

在前端要記住一些資料,我們會用到 cookie ,local storage或者 session storage,至於它們的不同點,可以參考 stackoverflow這篇文章,我們先來做一個有關 local storage 的服務 (service)

公用服務 (utility service)

第一步:造一個 src/app/services 目錄

mkdir servcies

第二步: 用 angular-cli 產生服務並自動註冊到 app.module.ts

ng generate service utils --module app 

第三步:加入 jsonwebtoken 來判斷 token 是否逾期

npm install @auth0/angular-jwt --save

記得使用這個套件,其它套件不能夠跟新的 HttpClientModule 相容

第四步:修改 app.module.ts,導入 JwtModule

// ... 省略
import { HttpClientModule } from '@angular/common/http';
import { JwtModule } from '@auth0/angular-jwt';

//... 省略
@NgModule({
    //... 
    imports: [
    //...
        HttpClientModule,
        JwtModule.forRoot({
            config: {
                tokenGetter: () => {
                    return localStorage.getItem('access_token');
                },
                whitelistedDomains: ['localhost:3000']
            }
        })
    ],
//...

記得同時導入 HttpClientModule 跟 JwtModule

第五步:修改 utils.service.ts

import { Injectable } from '@angular/core';
import { JwtHelperService } from '@auth0/angular-jwt';
export const TOKEN = 'access_token';
@Injectable()
export class UtilsService {
    constructor(
        private jwtHelper: JwtHelperService
    ) { }

    isTokenExpired(token: string = TOKEN): boolean {
        let jwtStr = this.getToken(token);
        if (jwtStr) {
            return this.jwtHelper.isTokenExpired(jwtStr);  // token expired?
        } else {
            return true;        // no token 
        }
    }

    writeToken(value: string, token: string = TOKEN) {
        localStorage.setItem(token, value);
    }

    getToken(token: string = TOKEN) {
        return localStorage.getItem(token);
    }

    removeToken(token: string = TOKEN) {
        if (this.getToken(token)) {
            localStorage.removeItem(token);
        }
    }
}

預設的 token 的關鍵(key) 為 access_token,為了可以寫入不同的關鍵,我們參數保留了 token 這個關鍵。
第六步: 將 index.ts 加入 src/app/services

export * from './utils.servcie';

修改使用者服務

第一步:導入公用服務

// ... 省略
import { UtilsService } from '../../services';

@Injectable()
export class UserService {
    loginStatus = new BehaviorSubject<boolean>(false);
    currentUser = new BehaviorSubject<User>(null);
    constructor(
        private http: HttpClient,
        private appConfig: AppConfig,
        private utils: UtilsService
    ) { }
// ... 省略

第二步:修改 login() 程式,後端驗證成功會傳回關鍵值 (value),如果使用者有勾選 記得我,我們用公用服務將它寫入 localstorage

// ... 省略
login(loginData): Observable<boolean> {
        return this.loginServer(loginData)
            .map((res: Response) => {
                if (res.success) {
                    this.loginStatus.next(true);
                    this.currentUser.next(loginData.username);
                    if (loginData.rememberMe) {
                        this.utils.writeToken(res.payload);
                    }
                    return true;
                } else {
                    return false;
                }
            },
//... 省略

第三步:修改 logout(),登出時將 token 清掉

   logout() {
        this.loginStatus.next(false);
        this.currentUser.next(null);
        this.utils.removeToken();
    }

第四步:加入 checkuser() 函數

// when startup
    checkUser(): Observable<boolean> {
        if (!this.utils.isTokenExpired()) {
            this.loginStatus.next(true);
            return of(true);
        } else {
            console.log('no token or token is expired');
            this.utils.removeToken();
            return of(false);
        }
    }

這個函數馬上會用到,基本上是先檢查 token 有沒有過期,沒有的話將 loginStatus 設為真

第五步:確認
做登入後檢查是否 localstorage 已經寫入,正常狀態如截圖
https://ithelp.ithome.com.tw/upload/images/20180105/20103574ORJdKJBSWj.png

啟動服務 (startup service)

接下來我們要在瀏覽器重新啟動時來檢查這個 token,我們先來做一個服務
第一步:產生 startup.servcie.ts 在 src/app/services 下

ng generate servcie startup --module app

第二步:修改程式

import { Injectable, Injector } from '@angular/core';
import { Router } from '@angular/router';
import { UtilsService } from '.';
import { UserService } from '../user/service/user.service';
@Injectable()
export class StartupService {
    constructor(
        private injector: Injector,
        private utils: UtilsService,
        private userService: UserService
    ) { }
    load(): Promise<any> {
        return new Promise((resolve, reject) => {
            return this.userService.checkUser()
                .subscribe(res => {
                    if (res) {
                        setInterval(() => {
                            this.checkStatus();
                        }, 1000 * 60 * 5)    // check current status every 5 min
                    }
                    resolve(res);
                }, err => {
                    console.log(err);
                    reject(err);
                });
        });
    }
    checkStatus() {
        if (this.utils.isTokenExpired()) {   // if token expired
            this.userService.logout();
            const router = this.injector.get(Router);
            router.navigate(['/']);
            console.log('logout due to token expired');
        }
    }
}
  1. startup 服務一定要建置一個 Promise 函數回給系統,這裡我們用 load()
  2. 在 load() 函數中利用使用者服務的 checkuser() 函數,如果 token 還沒過期,使用者服務的 loginStatus 會變成 true
  3. 每五分鐘檢查一下狀態 checkStatus(),如果逾期,做一個 logout() 的動作然後將頁面導回首頁
  4. 因為在 Startup 時無法直接用 DI (Depency Injection) Router,請參考 stackoverflow,我們需要用到Injector 來導入Router

第三步: 修改app.module.ts

//... 省略
import { NgModule, APP_INITIALIZER, Injector } from '@angular/core';
//... 省略
export function startupServiceFactory(startupService: StartupService): Function { return () => startupService.load(); }
// ... 省略
@NgModule({
    // ...
  providers: [
        UtilsService,
        StartupService,
        {
            provide: APP_INITIALIZER,
            useFactory: startupServiceFactory,
            deps: [StartupService, Injector],
            multi: true
        }
    ],
//... 省略

好了,現在當您登入系統,回到首頁,重新刷瀏覽器,應該會留在登入狀態(瀏覽列底色),但是看不到使用者名稱,也沒辦法登出,我們下次來解決這個問題。


上一篇
[ngrx/store-19] Angular 網站實例 - 使用者服務篇
下一篇
[ngrx/store-21] Angular 網站實例 - 記得我之二篇
系列文
ngrx/store 4 學習筆記 共 30 篇
圖片
  熱門推薦
圖片
{{ item.channelVendor }} | {{ item.webinarstarted }} |
{{ formatDate(item.duration) }}
直播中

尚未有邦友留言

立即登入留言