Node express JWT Api

什麼是 JWT?

JSON Web Tokens (JWT) 是一種開放式標準 (RFC 7519),用於以 JSON 物件的形式在各​​方之間安全地傳輸資訊。 JWT 通常用於身份驗證和授權目的。它們由三個部分組成:

  1. 標頭 HEADER:包含有關令牌的元數據,例如令牌的類型(JWT)和使用的雜湊演算法。
  2. 有效負載 PAYLOAD:包含聲明,即關於實體(通常是使用者)和附加資料的陳述。
  3. 簽章:用於驗證令牌的完整性和真實性。

cors 跨來源資源共用

跨來源資源共用(Cross-Origin Resource Sharing (CORS))是一種使用額外 HTTP 標頭令目前瀏覽網站的使用者代理 (en-US)取得存取其他來源(網域)伺服器特定資源權限的機制。當使用者代理請求一個不是目前文件來源——例如來自於不同網域(domain)、通訊協定(protocol)或通訊埠(port)的資源時,會建立一個跨來源 HTTP 請求(cross-origin HTTP request)
安裝cors

1
npm install cors --save

啟用所有 CORS 請求

  1. 載入express
  2. 載入cors
  3. use cors
1
2
3
4
5
6
7
8
9
10
11
12
13
var express = require('express')
var cors = require('cors')
var app = express()

app.use(cors())

app.get('/products/:id', (req, res, next)=> {
res.json({msg: '這對所有來源都支持 CORS!'})
})

app.listen(80, function () {
console.log('CORS-enabled web server listening on port 80')
})
### 為單個路由啟用 CORS
  1. 載入express
  2. 載入cors
  3. 在單一路由加入cors()
1
2
3
4
5
6
7
8
9
10
11
var express = require('express')
var cors = require('cors')
var app = express()

app.get('/products/:id', cors(), (req, res, next)=> {
res.json({msg: '為單個路由啟用 CORS 的'})
})

app.listen(80, function () {
console.log('這是為偵聽端口 80 的單個啟用了 RouteCORS 的 Web 服務器啟用了 CORS')
})

Configuring CORS=>配置 CORS

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
var express = require('express')
var cors = require('cors')
var app = express()

var corsOptions = {
origin: 'http://example.com',
// 選項成功狀態:一些舊版瀏覽器(IE11、各種 SmartTV)在 204 上卡住
optionsSuccessStatus: 200
}

app.get('/products/:id', cors(corsOptions), function (req, res, next) {
res.json({msg: 'This is CORS-enabled for only example.com.'})
})

app.listen(80, function () {
console.log('CORS-enabled web server listening on port 80')
})

默認配置相當於:

  • origin:配置 Access-Control-Allow-Origin CORS 標頭。
  • methods:配置 Access-Control-Allow-Methods CORS 標頭。要一個逗號分隔的字符串(例如:'GET,PUT,POST')或一個數組(例如:['GET', 'PUT', 'POST'])。
  • allowedHeaders配置 Access-Control-Allow-Headers CORS 標頭。 需要一個逗號分隔的字符串(例如:'Content-Type,Authorization')或一個數組(例如:['Content-Type', 'Authorization'])。 如果未指定,則默認反映請求的 Access-Control-Request-Headers 標頭中指定的標頭。
  • exposedHeaders配置 Access-Control-Expose-Headers CORS 標頭。 需要一個逗號分隔的字符串(例如:'Content-Range,X-Content-Range')或一個數組(例如:['Content-Range', 'X-Content-Range'])。 如果未指定,則不會公開任何自定義標頭。
  • credentials配置 Access-Control-Allow-Credentials CORS 標頭。 設置為 true 以傳遞標頭,否則將被省略。
  • maxAge:配置 Access-Control-Max-Age CORS 標頭。 設置為整數以傳遞標頭,否則將被省略。 preflightContinue:將 CORS 預檢響應傳遞給下一個處理程序。
  • preflightContinue:將 CORS 預檢響應傳遞給下一個處理程序。
  • optionsSuccessStatus:提供用於成功 OPTIONS 請求的狀態代碼,因為一些舊版瀏覽器(IE11、各種 SmartTV)會在 204 上阻塞。
1
2
3
4
5
6
{
"origin": "*",
"methods": "GET,HEAD,PUT,PATCH,POST,DELETE",
"preflightContinue": false,
"optionsSuccessStatus": 204
}

bcryptjs密碼加密

安裝bcryptjs bcryptjs
密碼加密,此套件的加密是不可逆的,所以沒有辦法從加密後的結果回推原始密碼,相對安全性提高非常多

1
npm install bcryptjs --save

異步

1
2
3
4
const bcrypt = require('bcrypt');
const saltRounds = 10;
const myPlaintextPassword = 's0/\/\P4$$w0rD';
const someOtherPlaintextPassword = 'not_bacon';

技術 1(在單獨的函數呼叫上產生鹽和雜湊值):
bcrypt.hash()

1
2
3
4
5
bcrypt.genSalt(saltRounds, function(err, salt) {
bcrypt.hash(myPlaintextPassword, salt, function(err, hash) {
// Store hash in your password DB.
});
});

技術 2(自動產生鹽和雜湊值):
bcrypt.hash()

1
2
3
bcrypt.hash(myPlaintextPassword, saltRounds, function(err, hash) {
// Store hash in your password DB.
});

這兩種技術達到相同的最終結果。

要檢查密碼:

1
2
3
4
5
6
7
// Load hash from your password DB.
bcrypt.compare(myPlaintextPassword, hash, function(err, result) {
// result == true
});
bcrypt.compare(someOtherPlaintextPassword, hash, function(err, result) {
// result == false
});

JWT 包含三個重要部分:Header、Payload、Signature。它們一起組合成一個標準結構:header.payload.signature.
客戶端通常在Authorization header 中附加 JWT 和 Bearer 前綴:

1
2
Authorization: Bearer [header].[payload].[signature]
Authorization: base64(Header) + base64(Payload) + base64(Signature)

或者僅在x-access-token標頭中:

1
x-access-token: [header].[payload].[signature]

Node.js express 和 MongoDB 用戶身份驗證

安裝 jsonwebtoken

1
2
3
npm install jsonwebtoken --save
//載入 jsonwebtoken
const jwt = require('jsonwebtoken')

產生 JWT
透過模組上的sign()方法可以產生一組 JWT,產生時需要將存放在 Token 當中的資料帶入payload參數中、密鑰帶入secretOrPrivateKey參數中:

1
jwt.sign(payload, secretOrPrivateKey, [options, callback])

options參數非必填,但透過帶入options物件能設定許多選項。例如:

  • algorithm:設定產生簽章要使用的雜湊演算法(預設: HS256)
  • expiresIn:設定 Token 多久後會過期(自動在 Payload 新增 exp)
  • noTimestamp:設定產生 JWD 時不會自動在 Payload 中新增iat時間

callback 參數非必填,但若要以非同步方式產生 JWD,可以提供一個 Callback 函式,Token 將能在 Callback 函式中取得。

1
2
3
4
// 設定密鑰
const SECRET = 'thisismynewproject'
// 建立 Token
const token = jwt.sign({ _id: user._id.toString() }, SECRET, { expiresIn: '1 day' })