Skip to content

Commit 3549f37

Browse files
authored
Merge pull request #24 from andy6804tw/Part17-get-personal-article(2)
done Part17-get-personal-article(2)
2 parents 82ebe31 + c18c2fe commit 3549f37

File tree

5 files changed

+154
-26
lines changed

5 files changed

+154
-26
lines changed

README.md

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -48,6 +48,9 @@
4848
- 用JWT取代傳統Session來驗證使用者身份
4949
- [Link 連結](https://github.com/andy6804tw/RESTful_API_start_kit/tree/Part15-User-jsonwebtoken)
5050
- [Tutorial 教學](/tutorials/Part15-User-jsonwebtoken.md)
51-
- (實作)使用JWT訪問API內容(1)
51+
- (實作)使用JWT訪問API內容()
5252
- [Link 連結](https://github.com/andy6804tw/RESTful_API_start_kit/tree/Part16-get-personal-article(1))
5353
- [Tutorial 教學](/tutorials/Part16-get-personal-article(1).md)
54+
- (實作)使用JWT訪問API內容(下)
55+
- [Link 連結](https://github.com/andy6804tw/RESTful_API_start_kit/tree/Part17-get-personal-article(2))
56+
- [Tutorial 教學](/tutorials/Part17-get-personal-article(2).md)

src/server/modules/article.module.js

Lines changed: 28 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -25,7 +25,6 @@ const selectArticle = () => {
2525
Article`
2626
, (error, result) => {
2727
if (error) {
28-
console.error('SQL error: ', error);
2928
reject(error); // 寫入資料庫有問題時回傳錯誤
3029
} else {
3130
resolve(result); // 撈取成功回傳 JSON 資料
@@ -47,7 +46,6 @@ const createArticle = (insertValues) => {
4746
} else {
4847
connection.query('INSERT INTO Article SET ?', insertValues, (error, result) => { // Article資料表寫入一筆資料
4948
if (error) {
50-
console.error('SQL error: ', error);
5149
reject(error); // 寫入資料庫有問題時回傳錯誤
5250
} else if (result.affectedRows === 1) {
5351
resolve(`新增成功! article_id: ${result.insertId}`); // 寫入成功回傳寫入id
@@ -60,15 +58,15 @@ const createArticle = (insertValues) => {
6058
};
6159

6260
/* Article PUT 修改 */
63-
const modifyArticle = (insertValues, productId) => {
61+
const modifyArticle = (insertValues, userId) => {
6462
return new Promise((resolve, reject) => {
6563
connectionPool.getConnection((connectionError, connection) => { // 資料庫連線
6664
if (connectionError) {
6765
reject(connectionError); // 若連線有問題回傳錯誤
6866
} else { // Article資料表修改指定id一筆資料
69-
connection.query('UPDATE Article SET ? WHERE article_id = ?', [insertValues, productId], (error, result) => {
67+
connection.query('UPDATE Article SET ? WHERE article_id = ?', [insertValues, userId], (error, result) => {
7068
if (error) {
71-
console.error('SQL error: ', error);// 寫入資料庫有問題時回傳錯誤
69+
// 寫入資料庫有問題時回傳錯誤
7270
reject(error);
7371
} else if (result.affectedRows === 0) { // 寫入時發現無該筆資料
7472
resolve('請確認修改Id!');
@@ -85,15 +83,15 @@ const modifyArticle = (insertValues, productId) => {
8583
};
8684

8785
/* Article DELETE 刪除 */
88-
const deleteArticle = (productId) => {
86+
const deleteArticle = (userId) => {
8987
return new Promise((resolve, reject) => {
9088
connectionPool.getConnection((connectionError, connection) => { // 資料庫連線
9189
if (connectionError) {
9290
reject(connectionError); // 若連線有問題回傳錯誤
9391
} else { // Article資料表刪除指定id一筆資料
94-
connection.query('DELETE FROM Article WHERE article_id = ?', productId, (error, result) => {
92+
connection.query('DELETE FROM Article WHERE article_id = ?', userId, (error, result) => {
9593
if (error) {
96-
console.error('SQL error: ', error);// 資料庫存取有問題時回傳錯誤
94+
// 資料庫存取有問題時回傳錯誤
9795
reject(error);
9896
} else if (result.affectedRows === 1) {
9997
resolve('刪除成功!');
@@ -110,16 +108,31 @@ const deleteArticle = (productId) => {
110108
/* Article GET JWT取得個人文章 */
111109
const selectPersonalArticle = (token) => {
112110
return new Promise((resolve, reject) => {
113-
jwt.verify(token, 'my_secret_key', (err, payload) => {
111+
// JWT解密驗證
112+
jwt.verify(token, 'my_secret_key', (err, decoded) => {
114113
if (err) {
115114
reject(err); // 驗證失敗回傳錯誤
116115
} else {
117-
/* ...撈取資料庫該用戶的所有文章
118-
...
119-
...
120-
...
121-
*/
122-
resolve(payload); // 驗證成功回傳 payload data
116+
// JWT 驗證成功 ->取得用戶 user_id
117+
const userId = decoded.payload.user_id;
118+
// JWT 驗證成功 -> 撈取該使用者的所有文章
119+
connectionPool.getConnection((connectionError, connection) => { // 資料庫連線
120+
if (connectionError) {
121+
reject(connectionError); // 若連線有問題回傳錯誤
122+
} else {
123+
connection.query( // Article 撈取 user_id 的所有值組
124+
'SELECT * FROM Article WHERE user_id = ?', [userId]
125+
, (error, result) => {
126+
if (error) {
127+
reject(error); // 寫入資料庫有問題時回傳錯誤
128+
} else {
129+
resolve(result); // 撈取成功回傳 JSON 資料
130+
}
131+
connection.release();
132+
}
133+
);
134+
}
135+
});
123136
}
124137
});
125138
});

src/server/modules/user.module.js

Lines changed: 2 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -26,7 +26,6 @@ const selectUser = () => {
2626
User`
2727
, (error, result) => {
2828
if (error) {
29-
console.error('SQL error: ', error);
3029
reject(error); // 寫入資料庫有問題時回傳錯誤
3130
} else {
3231
resolve(result); // 撈取成功回傳 JSON 資料
@@ -48,7 +47,6 @@ const createUser = (insertValues) => {
4847
} else {
4948
connection.query('INSERT INTO User SET ?', insertValues, (error, result) => { // User資料表寫入一筆資料
5049
if (error) {
51-
console.error('SQL error: ', error);
5250
reject(error); // 寫入資料庫有問題時回傳錯誤
5351
} else if (result.affectedRows === 1) {
5452
resolve(`新增成功! user_id: ${result.insertId}`); // 寫入成功回傳寫入id
@@ -69,7 +67,7 @@ const modifyUser = (insertValues, userId) => {
6967
} else { // User資料表修改指定id一筆資料
7068
connection.query('UPDATE User SET ? WHERE user_id = ?', [insertValues, userId], (error, result) => {
7169
if (error) {
72-
console.error('SQL error: ', error);// 寫入資料庫有問題時回傳錯誤
70+
// 寫入資料庫有問題時回傳錯誤
7371
reject(error);
7472
} else if (result.affectedRows === 0) { // 寫入時發現無該筆資料
7573
resolve('請確認修改Id!');
@@ -94,7 +92,7 @@ const deleteUser = (userId) => {
9492
} else { // User資料表刪除指定id一筆資料
9593
connection.query('DELETE FROM User WHERE user_id = ?', userId, (error, result) => {
9694
if (error) {
97-
console.error('SQL error: ', error);// 資料庫存取有問題時回傳錯誤
95+
// 資料庫存取有問題時回傳錯誤
9896
reject(error);
9997
} else if (result.affectedRows === 1) {
10098
resolve('刪除成功!');
@@ -119,7 +117,6 @@ const selectUserLogin = (insertValues) => {
119117
'SELECT * FROM User WHERE user_mail = ?',
120118
insertValues.user_mail, (error, result) => {
121119
if (error) {
122-
console.error('SQL error: ', error);
123120
reject(error); // 寫入資料庫有問題時回傳錯誤
124121
} else if (Object.keys(result).length === 0) {
125122
reject(new APPError.LoginError1()); // 信箱尚未註冊

src/server/routes/article.route.js

Lines changed: 7 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -13,18 +13,20 @@ router.route('/:article_id')
1313
.put(articleCtrl.articlePut) /** 修改 Article 值組 */
1414
.delete(articleCtrl.articleDelete); /** 刪除 Article 值組 */
1515

16+
/** 利用 Middleware 取得 Header 中的 Rearer Token */
1617
const ensureToken = (req, res, next) => {
1718
const bearerHeader = req.headers.authorization;
1819
if (typeof bearerHeader !== 'undefined') {
19-
const bearer = bearerHeader.split(' ');
20-
const bearerToken = bearer[1];
21-
req.token = bearerToken;
22-
next();
20+
const bearer = bearerHeader.split(' '); // 字串切割
21+
const bearerToken = bearer[1]; // 取得 JWT
22+
req.token = bearerToken; // 在response中建立一個token參數
23+
next(); // 結束 Middleware 進入 articleCtrl.articlePersonalGet
2324
} else {
24-
res.status(403).send(Object.assign({ code: 403 }, { message: '您尚未登入!' }));
25+
res.status(403).send(Object.assign({ code: 403 }, { message: '您尚未登入!' })); // Header 查無 Rearer Token
2526
}
2627
};
2728

29+
/** 取得某用戶 Article 所有值組 */
2830
router.get('/personal', ensureToken, articleCtrl.articlePersonalGet);
2931

3032

Lines changed: 113 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,113 @@
1+
---
2+
layout: post
3+
title: '[Node.js打造API] (實作)使用JWT來存取API內容(下)'
4+
categories: '2018iT邦鐵人賽'
5+
description: 'JSON Web Token'
6+
keywords: api
7+
---
8+
9+
## 本文你將會學到
10+
- 如何將 JWT 做解密驗證
11+
- 使用 HTTP header 中的 Authorization 傳送 Bearer Token
12+
- 實作一個 API 撈取並顯示自己所發佈過的文章(完成後半部資料庫撈取文章)
13+
14+
## 前言
15+
昨天已經成功的使用 HTTP header 中的 Authorization 並傳送 Bearer Token 到 Middleware 做字串切割後傳到 Controller 取得 API Token,最後傳送到 Module 做 JWT 驗證並回應 Payload 資料,今天就來繼續完成 API 撈取並顯示自己所發佈過的文章的資料庫存取部分,JWT 驗證成功後會取得一個物件(Object)這邊稱他 decoded 格式如下:
16+
17+
```json
18+
//decoded物件
19+
{
20+
"payload": {
21+
"user_id": 1,
22+
"user_name": "Andy10",
23+
"user_mail": "andy@gmail.com"
24+
},
25+
"exp": 1515811450,
26+
"iat": 1515810550
27+
}
28+
```
29+
30+
我們要取得 `decoded` 中的 `user_id` 資訊並拿 `user_id` 去撈取資料表中的所有 Article 資料,這就是我們今天要實作的內容。
31+
32+
## 事前準備
33+
今天要繼續實作的程式是延續 [[Node.js打造API] (實作)使用JWT來存取API內容(上)](https://andy6804tw.github.io/2018/01/12/get-personal-article(1)/) 的專案繼續實作,想跟著今天的實作可以先下載下面的整包程式,記得要先 `yarn install` 將整個依賴的 Node.js 組件安裝回來。
34+
35+
程式碼:https://github.com/andy6804tw/RESTful_API_start_kit/releases/tag/V16.0.0
36+
37+
## 修改 article.module.js
38+
昨天已經建立了 `selectPersonalArticle` 函式並且成功的接收 controller 傳過來的 API Token 並將它做 JWT 解密驗證,今天繼續完成後面撈取資料庫部分(昨日註解的地方),首先取得 Payload 裡的用戶 id 因為他是用一個物件(Object)包起來所以我們取得資料的方式為 `decoded.payload.user_id` ,這樣就能知道是要撈取哪一位使用者的文章了!最後我們建立一個資料庫連線並且用 SQL 語法去撈取(SELECT) Article 資料表裡的所有欄位(*)並設立條件(WHERE)當 `user_id` 為變數 `userId`,這裡用問號方式來實作後面的中括號變數就是相對應的值,當然你也可以使用 ES6 的樣板字串(Template literals) \`SELECT * FROM Article WHERE user_id = ${userId}\` ,使用「\`」能將字串與程式變數同時寫在一起 `${變數}` 同時也能使用多行字串,最後資料庫撈取成功後就回傳他的結果。
39+
40+
<img src="/images/posts/it2018/img1070113-7.png">
41+
42+
```js
43+
...
44+
/* Article GET JWT取得個人文章 selectPersonalArticle */
45+
// JWT 驗證成功 ->取得用戶 user_id
46+
const userId = decoded.payload.user_id;
47+
// JWT 驗證成功 -> 撈取該使用者的所有文章
48+
connectionPool.getConnection((connectionError, connection) => { // 資料庫連線
49+
if (connectionError) {
50+
reject(connectionError); // 若連線有問題回傳錯誤
51+
} else {
52+
connection.query( // Article 撈取 user_id 的所有值組
53+
'SELECT * FROM Article WHERE user_id = ?', [userId]
54+
, (error, result) => {
55+
if (error) {
56+
reject(error); // 寫入資料庫有問題時回傳錯誤
57+
} else {
58+
resolve(result); // 撈取成功回傳 JSON 資料
59+
}
60+
connection.release();
61+
}
62+
);
63+
}
64+
});
65+
...
66+
```
67+
68+
## 測試
69+
70+
#### 1. POST多筆文章資料
71+
將程式碼 `yarn build``yarn start` 後,開啟Postman在網址列輸入 `http://127.0.0.1:3000/api/article` 並選擇 POST 請求方式,之後將所有資料 `user_id``article_title``article_tag``article_content` 放至 `Body > raw > 選擇 JSON(application/json)` 並用物件 JSON 型態包裝起來,最後按 Send 送出,你可以試著重複新增多筆資料和不同使用者的文章,照理來說這隻 API 應該也要利用 JWT 來取得 `user_id` 但為了方便測試請各位先自行手動填入 `user_id` 記住使用者 id 要與資料庫內的 `user_id` 相對應。
72+
73+
74+
```json
75+
{
76+
"user_id": 1,
77+
"article_title": "[Day-3] Node.js 入門介紹",
78+
"article_tag": "2018鐵人賽",
79+
"article_content": "何謂 Node.js Node.js 是以 JavaScript 語言為基礎,是一個開放的原始碼 (Open Source) 的應用程式框架 (Applicat..."
80+
}
81+
```
82+
<img src="/images/posts/it2018/img1070113-5.png">
83+
<img src="/images/posts/it2018/img1070113-6.png">
84+
85+
#### 2. 登入
86+
新增多筆文章後在 Postman 網址列輸入 `http://127.0.0.1:3000/api/user/login` 並選擇 POST 請求方式,接下來是要放一筆當時建立用戶的信箱與密碼至 `Body > raw > 選擇 JSON(application/json)` 並用物件 JSON 型態包裝起來,完成後送出取得 API Token ,最後再拿此 API Token 來訪問今天實作的 API 內容。
87+
88+
```json
89+
{
90+
"user_mail":"andy@gmail.com",
91+
"user_password":"password10"
92+
}
93+
```
94+
95+
<img src="/images/posts/it2018/img1070113-1.png">
96+
<img src="/images/posts/it2018/img1070113-2.png">
97+
98+
#### 3. JWT驗證並訪問API內容
99+
在 Postman 網址列輸入 `http://127.0.0.1:3000/api/article/personal` 後選擇 GET 並點選 Headers 並將 Key 放上 Authorization ,而 Value 放上 Bearer+JWT(注意記得空白隔開),如下。
100+
101+
```
102+
Key: Authorization
103+
Value: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJwYXlsb2FkIjp7InVzZXJfaWQiOjEsInVzZXJfbmFtZSI6IkFuZHkxMCIsInVzZXJfbWFpbCI6ImFuZHlAZ21haWwuY29tIn0sImV4cCI6MTUxNTgxNDc0NywiaWF0IjoxNTE1ODEzODQ3fQ.evjIOCsOwolFEn3Nj4BESlQ-OH-JPlwLnTZFgcNOoWc
104+
```
105+
106+
<img src="/images/posts/it2018/img1070113-3.png">
107+
108+
**JWT驗證成功並取得該用戶的所有文章**
109+
110+
我們利用 JWT 解密並取得 Payload 中的 `user_id` 並知道是要撈取資料庫中哪位使用者的資料,最後將所有文章值組列出來。
111+
112+
113+
<img src="/images/posts/it2018/img1070113-4.png">

0 commit comments

Comments
 (0)