前面完成了讓前端記住我的功能,但在實作的過程中,我們要的可能不只是登入狀態,今天讓我們來看一個例子。
假設我們要讓前端也記住登入使用者的名稱(或者其他資訊),我們又不要直接用明碼將這個資訊放在 localstorage
中,希望將有些資訊放在 token 中,因為 token 是亂碼,一般人是無法解讀的,也無法竄改。
這個資訊還是要透過後端來解析,因為後端才有解這個 token 的鑰匙 (SECRET),我們先來做這個部分
直接看程式
/* get user from token */
router.post('/currentUser', (req, res) => {
var token = req.body.token;
if (token) {
jwt.verify(token, SECRET, function (err, decoded) {
if (err) {
res.status(400).send({ success: false})
} else {
res.send({success: true, payload: decoded.username});
}
})
} else {
res.status(400).send({ success: false, payload: 'no token'});
}
})
之前我們用 let token = jwt.sign({ username: username}, SECRET, {'expiresIn': '1h'} );
來將使用者名稱包進 token 裡,這裡我們就可以用 jwt.verify(token, SECRET, function (err, decoded) {}
來解析 token, 解析成功的話就送回 decoded.username
給前端,這樣前端就可以來使用這個 API。
實際上,我們通常會將 user id
放在 token 裡,後端接到 id 後會到資料庫找出對應資料,再傳給前端,這裡為了簡單起見,不使用到資料庫,直接帶出資料。
回到前端的使用者服務,我們加入一個新的服務,getUser()
,程式如下
// get user from server
getUserFromServer(): Observable<User> {
if (!this.utils.isTokenExpired()) {
const token = this.utils.getToken();
return this.http.post(this.appConfig.apiUrl + '/users/currentUser', { 'token': token })
.map((res: Response) => {
if (res.success) {
return res.payload;
} else {
return null;
}
})
} else {
return of(null);
}
}
getUser() {
this.getUserFromServer()
.subscribe(res => {
this.currentUser$.next(res);
},
(err: HttpErrorResponse) => {
if (err.error instanceof Error) {
console.log('client-side error');
} else {
console.log('server-side error');
}
})
}
getUser()
呼叫 getUserFromServer()
getUserFromServer()
使用 HttpClient 來向後端連結,成功的話回傳一個 Observable<User>
給 getUser()
getUser()
將它放進 currentUser$
這個BehaviorSubject
, push 給 subscribers,對了,習慣上我們會在 Observable
的變數後加入 '$',使用上會更清楚。checkUser()
記得前面在 startup.service.ts
中,我們會呼叫 使用者服務的 checkUser()
,
//... 省略
load(): Promise<any> {
return new Promise((resolve, reject) => {
return this.userService.checkUser()
.subscribe(res => {
if (res) {
//...
所以我們可以將 getUser()
放進這個函數中,這樣在瀏覽器重新刷的時候就會順便帶進我們要的資料,程式如下
// when startup
checkUser(): Observable<boolean> {
if (!this.utils.isTokenExpired()) {
this.loginStatus$.next(true);
this.getUser();
return of(true);
} else {
console.log('no token or token is expired');
this.utils.removeToken();
return of(false);
}
}
現在如果登入後,回到首頁,重新刷瀏覽器,應該會得到下面的截圖,也就是跟之前一樣的畫面
至此,我們完成了使用 Angular 的服務來連結後端,完成使用者登入的功能,也儲存了使用者登入的狀態以及一些資訊,回顧一下使用者狀態
接下來,我們要完成會員功能,也就是登入狀態下的會員,可以到會員專屬頁面來看報告