關於接口的安全驗證
- 基於 Session 的安全驗證
- Session 存儲在服務器,用戶用戶比較少的話是一種簡單的安全驗證機制,但是涉及到跨域 的話需要進行一些配置
- 用戶量非常非常大的話會耗費一定的服務器資源(中小項目不需要考慮)
- 對請求參數進行加密的簽名驗證
- 涉及公鑰、私鑰、簽名等
- JWT
- JWT 全稱 JSON Web Token,是目前比較流行的另一種跨域身份驗證解決方案
- 也是被很多人用壞的一種安全驗證機制,具體需要看項目是否適合
關於 JWT的驗證的流程
- 客戶端請求接口登錄,登錄成功後,服務端通過jwt生成token返回給客戶端
- 客戶端訪問需要登錄後才能訪問的接口的時候,需要在請求接口的時候攜帶token,服務端獲取token調用jwt驗證token的合法性,驗證不通過則拒絕訪問
Nodejs 中使用 JWT 實現接口的安全驗證
簽發 token
安裝 jsonwebtoken $ cnpm install jsonwebtoken --save
簽發:
var jwt = require('jsonwebtoken');
router.get('/login', function (req, res, next) {
// 三個參數:用戶對象,簽名字符串(最好定義在配置中),過期時間
var token = jwt.sign({ name: '張三' }, 'this is sign',{ expiresIn:60*60*24});
res.send(token);
});
Axios 訪問基於 Jwt 的接口
// 存儲服務器簽發的token ...
var token = ''; // 這是保存的服務器簽發的token, 僞代碼
// 帶token請求接口
axios.get("http://localhost:3000/api/list", {
auth: {
username: token,
password:''
}
})
.then(function(response) {
// handle success
console.log(response); })
.catch(function(error) {
// handle error
console.log(error);
})
.finally(function() {
// always executed
});
備註:在不同的框架中如:react,vue,angular, 需要封裝通用攔截器,在攔截器中使用,以上僅作爲演示
獲取請求頭裏面的 token
安裝 $ cnpm install basic-auth --save
獲取
router.get('/addressList', function (req, res, next) {
var token = auth(req); // 這裏的auth就是 basic-auth
})
驗證客戶端傳過來 token 的合法性
router.get('/addressList', function (req, res, next) {
var token = auth(req);
if(!token) {
res.send('沒有權限');
}
try {
var v = jwt.verify(token.name, 'this is sign'); // 驗證token
// console.log(v);
res.send(v ? "有權限" : "沒有權限")
} catch (error) {
res.send(error);
}
});
備註:上面僅作爲簡單演示,最好做成一箇中間件來處理權限驗證
關於 Jwt 的一些問題
- JWT 默認是不加密,但也是可以加密的。生成原始 Token 以後,可以用密鑰再加密一次。
- JWT 不加密的情況下,不能將祕密數據寫入 JWT。
- JWT 不僅可以用於認證,也可以用於交換信息。有效使用 JWT,可以降低服務器查詢 數據庫的次數。
- JWT 的最大缺點是,由於服務器不保存 session 狀態,因此無法在使用過程中廢止某個token,或者更改 token 的權限。也就是說,一旦JWT簽發了,在到期之前就會始終有效,除非服務器部署額外的邏輯。
- JWT 本身包含了認證信息,一旦泄露,任何人都可以獲得該令牌的所有權限。爲了減少盜用,JWT的有效期應該設置得比較短。對於一些比較重要的權限,使用時應該再次對用戶進行認證。
- 爲了減少盜用,JWT 不應該使用 HTTP 協議明碼傳輸,要使用 HTTPS 協議傳輸。
關於jwt的二次簽名驗證
- 對於某一個接口,如: /api/address?uid=1&pid=2, 對請求的參數進行簽名, “uid=1&pid=2”,通過算法處理 var sign = md5(“uid=1&pid=2” + 私鑰) , 這個私鑰可以是個md5加密後的登錄密碼
- 這個時候客戶端會有這三個東西: token, 請求的url參數, sign (也就是 前面的password)
- 服務端獲取客戶端攜帶的token以及url上的參數, 然後比如根據uid查找用戶密碼, 計算出私鑰(私鑰=md5(用戶密碼)), 然後計算出sign
- 對比客戶端傳遞過來的sign和服務端計算出的sign是否一致, 如果一致則驗證通過
- 當另一個接口被請求的時候,如:/api/xxx?uid=1&tid=3 同樣的道理,每次因爲參數不同,計算出的sign都是不一樣的,當然如果沒有參數,接口本身其實也可以當做參數處理, 服務器可以通過相同的算法來進行對比,這樣可以進一步阻止了客戶端權限的盜用
- 當然道高一尺魔高一丈,沒有絕對的安全,如果涉及到一些支付相關的業務, 儘量利用二次加密和https以及儘可能小的過期時間,以及做一些機制對token進行續簽等