-
Notifications
You must be signed in to change notification settings - Fork 10
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
1 parent
dcfae96
commit 3821b8e
Showing
5 changed files
with
172 additions
and
2 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,78 @@ | ||
--- | ||
layout: post | ||
title: '[Node.js打造API] (實作)用JWT取代傳統Session來驗證使用者身份' | ||
categories: '2018iT邦鐵人賽' | ||
description: 'JSON Web Token' | ||
keywords: api | ||
--- | ||
|
||
## 本文你將會學到 | ||
- 了解 JWT 運作原理 | ||
- 實作使用者登入並取得一組 API Token | ||
|
||
## 前言 | ||
為什麼要有 API Token 呢?各位可以想想若今天我要存取特別資料例如交易紀錄或是發文紀錄,這些資料都有獨特性也就是這些資料只有你能做存取與修改,所以在訪問某些重要的 API 前就必須要有一個 Token 來驗證你是否有權限來訪問裡面的資料,那我們該怎去實作呢?早期作法就是使用 Session 並且生成一個 Session 的 Token 我們稱 SessionID,所謂 SessionID 就是一個既不會重複,又不容易被找到規律以免被仿造的字符串,你可以自己隨機產生一組字串,可能用當下時間戳記、年、月、日、時、分、秒來當種子搭配英文26個字母外加標點符號經亂數搗亂後所產生的一組字串可能長度30、20之類的,每次訪問某特別 API 前就由 SessionID 來做 Token 驗證,但這不是今天的重點!今天要用一個比較新的方法而且更方便更快速的來產生我們要的 API Token 就是 JWT ! | ||
|
||
## 何謂 JWT | ||
JWT 是 JSON Web Token 的縮寫,通常用來解決身份認證的問題,JWT 是一個很長的 base64 字串在這字串中分為三個部分別用點號來分隔,第一個部分為 `Header` ,裡面分別儲存型態和加密方法,通常系統是預設 `HS256` 雜湊演算法來加密,官方也提供許多演算法加密也可以手動更改加密的演算法,第二部分為 payload,它和 Session 一樣,可以把一些自定義的數據存儲在 `Payload` 裡例如像是用戶資料,第三個部分為 `Signature`,做為檢查碼是為了預防前兩部分被中間人偽照修改或利用的機制。 | ||
|
||
- Header(標頭): 用來指定 hash algorithm(預設為 HMAC SHA256) | ||
- Payload(內容): 可以放一些自己要傳遞的資料 | ||
- Signature(簽名): 為簽名檢查碼用,會有一個 serect string 來做一個字串簽署 | ||
|
||
把上面三個用「.」接起來就是一個完整的 JWT 了!更多詳細內容可以參考[這篇](https://auth0.com/blog/critical-vulnerabilities-in-json-web-token-libraries/)文章。 | ||
|
||
``` | ||
eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJ1c2VyX25hbWUiOiJBbmR5MTAiLCJ1c2VyX21haWwiOiJhbmR5QGdtYWlsLmNvbSIsInVzZXJfcGFzc3dvcmQiOiJwYXNzd29yZDEwIiwiaWF0IjoxNTE1MTQwNDg0fQ.P41UlFdYNIho2EA8T5k9iNK0EMC-Wn06RKk_0FFNjLo | ||
``` | ||
|
||
<img src="/images/posts/it2018/img1070111-1.png"> | ||
|
||
流程: 使用者登入 -> 產生 API Token -> 進行 API 路徑存取時先 JWT 驗證 -> 驗證成功才允許訪問該 API | ||
|
||
|
||
## 安裝 jsonwebtoken | ||
安裝 Node.js 的 jsonwebtoken 組件。 | ||
|
||
```bash | ||
yarn add jsonwebtoken | ||
``` | ||
|
||
## 修改 user.module.js | ||
開啟 `user.module.js` 並修改 `selectUserLogin` 函式中登入成功的地方,首先建立一個 payload 內容為你要傳遞的資料,這邊放入使用者的資訊包含 id、姓名、信箱,密碼這邊就不用存了一方面也是安全,建立好 payload 後就可以利用 jsonwebtoken 組件 來產生一個 Token 囉!我們使用 `jwt.sign()` 來取得 Token ,該方法有兩個參數第一部份是 Payload + 狀態資料,所謂的狀態資料有 expiresIn、notBefore、audience、subject、issuer,其中我們有使用 expiresIn(exp) 這是設定 Token 的時效性,格式為時間戳記這邊範例就以15分鐘為例,第二個參數為 Signature 是個字串型態的簽署金鑰,這個金鑰是保密的也只存放在後端不能前端用戶知道,否則會被不法人士利用來修改內容,此秘密金鑰在最後的 API 驗證才會使用到。 | ||
|
||
最後取得了 Token 就回傳結果,這邊使用 ES6 的物件生成寫法 `Object.assign()` 裡面用大括號包起來內容回傳 HTTP 狀態碼、登入訊息、和一組 API Token。 | ||
|
||
<img src="/images/posts/it2018/img1070111-2.png"> | ||
|
||
```js | ||
/** user.module.js | ||
selectUserLogin 程式片段 **/ | ||
...略 | ||
// 產生 JWT | ||
const payload = { | ||
user_id: result[0].user_id, | ||
user_name: result[0].user_name, | ||
user_mail: result[0].user_mail | ||
}; | ||
// 取得 API Token | ||
const token = jwt.sign({ payload, exp: Math.floor(Date.now() / 1000) + (60 * 15) }, 'my_secret_key'); | ||
resolve(Object.assign({ code: 200 }, { message: '登入成功', token })); // 登入成功 | ||
...略 | ||
``` | ||
|
||
## 登入測試 | ||
將程式碼 `yarn build` 再 `yarn start` 後,開啟Postman在網址列輸入 `http://127.0.0.1:3000/api/user/login` 並選擇 POST 請求方式,接下來是要放入修改的內容,`Body > raw > 選擇 JSON(application/json)`,將信箱與密碼用 JSON 格式送出。 | ||
|
||
**登入成功** | ||
|
||
輸入一筆當時建立用戶的信箱與密碼,登入成功後會取得一個 JSON 物件,包含 HTTP 狀態碼、登入訊息、和一組 API Token。 | ||
|
||
```json | ||
{ | ||
"user_mail":"[email protected]", | ||
"user_password":"password10" | ||
} | ||
``` | ||
|
||
<img src="/images/posts/it2018/img1070111-3.png"> |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -754,6 +754,10 @@ base64-js@^1.0.2: | |
version "1.2.1" | ||
resolved "https://registry.yarnpkg.com/base64-js/-/base64-js-1.2.1.tgz#a91947da1f4a516ea38e5b4ec0ec3773675e0886" | ||
|
||
[email protected], base64url@^2.0.0: | ||
version "2.0.0" | ||
resolved "https://registry.yarnpkg.com/base64url/-/base64url-2.0.0.tgz#eac16e03ea1438eff9423d69baa36262ed1f70bb" | ||
|
||
basic-auth@~2.0.0: | ||
version "2.0.0" | ||
resolved "https://registry.yarnpkg.com/basic-auth/-/basic-auth-2.0.0.tgz#015db3f353e02e56377755f962742e8981e7bbba" | ||
|
@@ -906,6 +910,10 @@ browserslist@^2.1.2: | |
caniuse-lite "^1.0.30000770" | ||
electron-to-chromium "^1.3.27" | ||
|
||
[email protected]: | ||
version "1.0.1" | ||
resolved "https://registry.yarnpkg.com/buffer-equal-constant-time/-/buffer-equal-constant-time-1.0.1.tgz#f8e71132f7ffe6e01a5c9697a4c6f3e48d5cc819" | ||
|
||
buffer-xor@^1.0.3: | ||
version "1.0.3" | ||
resolved "https://registry.yarnpkg.com/buffer-xor/-/buffer-xor-1.0.3.tgz#26e61ed1422fb70dd42e6e36729ed51d855fe8d9" | ||
|
@@ -1313,6 +1321,13 @@ ecc-jsbn@~0.1.1: | |
dependencies: | ||
jsbn "~0.1.0" | ||
|
||
[email protected]: | ||
version "1.0.9" | ||
resolved "https://registry.yarnpkg.com/ecdsa-sig-formatter/-/ecdsa-sig-formatter-1.0.9.tgz#4bc926274ec3b5abb5016e7e1d60921ac262b2a1" | ||
dependencies: | ||
base64url "^2.0.0" | ||
safe-buffer "^5.0.1" | ||
|
||
[email protected]: | ||
version "1.1.1" | ||
resolved "https://registry.yarnpkg.com/ee-first/-/ee-first-1.1.1.tgz#590c61156b0ae2f4f0255732a158b266bc56b21d" | ||
|
@@ -2320,6 +2335,21 @@ jsonify@~0.0.0: | |
version "0.0.0" | ||
resolved "https://registry.yarnpkg.com/jsonify/-/jsonify-0.0.0.tgz#2c74b6ee41d93ca51b7b5aaee8f503631d252a73" | ||
|
||
jsonwebtoken@^8.1.0: | ||
version "8.1.0" | ||
resolved "https://registry.yarnpkg.com/jsonwebtoken/-/jsonwebtoken-8.1.0.tgz#c6397cd2e5fd583d65c007a83dc7bb78e6982b83" | ||
dependencies: | ||
jws "^3.1.4" | ||
lodash.includes "^4.3.0" | ||
lodash.isboolean "^3.0.3" | ||
lodash.isinteger "^4.0.4" | ||
lodash.isnumber "^3.0.3" | ||
lodash.isplainobject "^4.0.6" | ||
lodash.isstring "^4.0.1" | ||
lodash.once "^4.0.0" | ||
ms "^2.0.0" | ||
xtend "^4.0.1" | ||
|
||
jsprim@^1.2.2: | ||
version "1.4.1" | ||
resolved "https://registry.yarnpkg.com/jsprim/-/jsprim-1.4.1.tgz#313e66bc1e5cc06e438bc1b7499c2e5c56acb6a2" | ||
|
@@ -2329,6 +2359,23 @@ jsprim@^1.2.2: | |
json-schema "0.2.3" | ||
verror "1.10.0" | ||
|
||
jwa@^1.1.4: | ||
version "1.1.5" | ||
resolved "https://registry.yarnpkg.com/jwa/-/jwa-1.1.5.tgz#a0552ce0220742cd52e153774a32905c30e756e5" | ||
dependencies: | ||
base64url "2.0.0" | ||
buffer-equal-constant-time "1.0.1" | ||
ecdsa-sig-formatter "1.0.9" | ||
safe-buffer "^5.0.1" | ||
|
||
jws@^3.1.4: | ||
version "3.1.4" | ||
resolved "https://registry.yarnpkg.com/jws/-/jws-3.1.4.tgz#f9e8b9338e8a847277d6444b1464f61880e050a2" | ||
dependencies: | ||
base64url "^2.0.0" | ||
jwa "^1.1.4" | ||
safe-buffer "^5.0.1" | ||
|
||
kind-of@^3.0.2: | ||
version "3.2.2" | ||
resolved "https://registry.yarnpkg.com/kind-of/-/kind-of-3.2.2.tgz#31ea21a734bab9bbb0f32466d893aea51e4a3c64" | ||
|
@@ -2390,6 +2437,34 @@ lodash.cond@^4.3.0: | |
version "4.5.2" | ||
resolved "https://registry.yarnpkg.com/lodash.cond/-/lodash.cond-4.5.2.tgz#f471a1da486be60f6ab955d17115523dd1d255d5" | ||
|
||
lodash.includes@^4.3.0: | ||
version "4.3.0" | ||
resolved "https://registry.yarnpkg.com/lodash.includes/-/lodash.includes-4.3.0.tgz#60bb98a87cb923c68ca1e51325483314849f553f" | ||
|
||
lodash.isboolean@^3.0.3: | ||
version "3.0.3" | ||
resolved "https://registry.yarnpkg.com/lodash.isboolean/-/lodash.isboolean-3.0.3.tgz#6c2e171db2a257cd96802fd43b01b20d5f5870f6" | ||
|
||
lodash.isinteger@^4.0.4: | ||
version "4.0.4" | ||
resolved "https://registry.yarnpkg.com/lodash.isinteger/-/lodash.isinteger-4.0.4.tgz#619c0af3d03f8b04c31f5882840b77b11cd68343" | ||
|
||
lodash.isnumber@^3.0.3: | ||
version "3.0.3" | ||
resolved "https://registry.yarnpkg.com/lodash.isnumber/-/lodash.isnumber-3.0.3.tgz#3ce76810c5928d03352301ac287317f11c0b1ffc" | ||
|
||
lodash.isplainobject@^4.0.6: | ||
version "4.0.6" | ||
resolved "https://registry.yarnpkg.com/lodash.isplainobject/-/lodash.isplainobject-4.0.6.tgz#7c526a52d89b45c45cc690b88163be0497f550cb" | ||
|
||
lodash.isstring@^4.0.1: | ||
version "4.0.1" | ||
resolved "https://registry.yarnpkg.com/lodash.isstring/-/lodash.isstring-4.0.1.tgz#d527dfb5456eca7cc9bb95d5daeaf88ba54a5451" | ||
|
||
lodash.once@^4.0.0: | ||
version "4.1.1" | ||
resolved "https://registry.yarnpkg.com/lodash.once/-/lodash.once-4.1.1.tgz#0dd3971213c7c56df880977d504c88fb471a97ac" | ||
|
||
lodash@^4.14.0, lodash@^4.17.4, lodash@^4.2.0, lodash@^4.3.0, lodash@^4.9.0: | ||
version "4.17.4" | ||
resolved "https://registry.yarnpkg.com/lodash/-/lodash-4.17.4.tgz#78203a4d1c328ae1d86dca6460e369b57f4055ae" | ||
|
@@ -2534,6 +2609,10 @@ [email protected]: | |
version "2.0.0" | ||
resolved "https://registry.yarnpkg.com/ms/-/ms-2.0.0.tgz#5608aeadfc00be6c2901df5f9861788de0d597c8" | ||
|
||
ms@^2.0.0: | ||
version "2.1.1" | ||
resolved "https://registry.yarnpkg.com/ms/-/ms-2.1.1.tgz#30a5864eb3ebb0a66f2ebe6d727af06a09d86e0a" | ||
|
||
[email protected]: | ||
version "0.0.7" | ||
resolved "https://registry.yarnpkg.com/mute-stream/-/mute-stream-0.0.7.tgz#3075ce93bc21b8fab43e1bc4da7e8115ed1e7bab" | ||
|
@@ -3735,7 +3814,7 @@ write@^0.2.1: | |
dependencies: | ||
mkdirp "^0.5.1" | ||
|
||
xtend@^4.0.0: | ||
xtend@^4.0.0, xtend@^4.0.1: | ||
version "4.0.1" | ||
resolved "https://registry.yarnpkg.com/xtend/-/xtend-4.0.1.tgz#a5c6d532be656e23db820efb943a1f04998d63af" | ||
|
||
|