Authentication and Authorization
Authentication |
Authorization |
識別對資料庫來說有效的使用者 |
識別有效的使用者中他們能做什麼操作 |
比喻: 例如一家公司的員工被允許進入辦公室 |
比喻: 公司的員工帳號允許進入辦公室並處理訂單 |
- 每個user有專屬的roles,roles包含了一個或多個privileges,而privileges定義了該user能進入那些database和進行那些操作
- Privileges
- Resources: 能進入的database、collections
- Actions: 能進行的操作,例如insert()
- 資料庫的使用者可以有不同的分工 (Roles),被給予的權限也不一樣(可以避免意外的發生)
Administrator |
Developer/Your app |
Data Scientist |
能管理資料庫的設定,新增使用者 |
需要進行新增、刪除、閱讀等操作(CRUD) |
需要能夠抓取資料 |
不需要新增或取用資料 |
不需要新增使用者或管理資料庫設定 |
不需要進行新增、刪除等操作 |
開啟使用者認證功能的方式啟動MongoDB
1 2 3
| sudo mongod --auth //然後進入mongo shell mongo
|
1 2 3 4 5
|
mongo -u "username" -p "password"
db.auth("username","password")
|
- 在沒有建立任何一個user的情況下連接mongodb時,mongodb允許在localhost連接的狀態下新增user,且該user擁有完整的功能
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18
| use admin db.createUser({user: "god", pwd: "god", roles: ["userAdminAnyDatabase"]})
db.auth('god', 'god')
mongo -u god -p god --authenticationDatabase admin
use shop db.createUser({user: "user1", pwd: "user1", roles: ["readWrite"]})
db.auth({'user1', 'user1'})
db.logout()
mongo -u user1 -p user1 --authenticationDatabase shop
|
有哪些roles可以設定以及該roles有哪些權限可以參考官方文件
- 有時一個帳號需要可以操作兩個database,可以使用updateUser去設定user權限
- 有個重要的原則是,該user是在哪個database被建立的,就必須要在該database登入和登出
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34
| use admin db.auth('god', 'god') use shop db.updateUser("user1", {roles: ["readWrite", {role: "readWrite", db: "blog"}]})
db.getUser("user1")
"_id" : "shop.user1", "userId" : UUID("20ef4181-c13b-4985-ab2f-c731ed577cb5"), "user" : "user1", "db" : "shop", "roles" : [ { "role" : "readWrite", "db" : "shop" }, { "role" : "readWrite", "db" : "blog" } ], "mechanisms" : [ "SCRAM-SHA-1", "SCRAM-SHA-256" ] }
use admin db.logout() use shop db.auth("user1", "user1") use blog
|
Encryption
- mongodb使用TLS/SSL加密方式建立mongodb與mongodb driver之間的連接
- 一旦加密建立,沒有特殊key的話就不能隨意與mongodb連線(在cmd下mongo會被擋住),也可以防止外部的惡意攻擊者的違法連線
- 詳細SSL的相關設定參考官網
其他參考文件
Official “Encryption at Rest” Docs連結
Official Security Checklist連結
Perfromance, Fault Tolerancy&Deployment
Capped collection
- capped collection是一種特殊的collection,可以設定他的最大儲存大小和儲存的document數,以確保該collection保持在一個簡潔的狀態下
- capped collection裡的資料會保證一定按照新增的順序所排序(一般的collection則不保證)
1 2 3
|
db.createCollection("capped", {capped: true, size: 10000, max: 3})
|
Replica Sets
- 當我們寫入資料進資料庫時,實際上是先透過mongo shell傳達給mongodb server,mongodb server在傳達給primary node寫入資料(node其實也是mongodb server)
- 我們可以增加更多的node,這些node被稱為Secondary node,所有的node集合稱為replica sets
- 在一般的情況下,當insert、write等操作時都是跟primary node溝通(自動如此),但primary node會異步的把資料複製給secondary node
- 在我們read資料時,如果primary node因為一些原因離線了,可以直接連接到secondary node,並把該node視為新的primary node來讀取資料,透過primary node就可以不只是read也可以write資料(write一定要透過primary node)
- 這個機制提供了server的容錯率
- 透過使用者主動設定,可以在read時直接跳過primary node與secondary node溝通,這樣可以確保在read資料時能以最快的速度達成目的,假設每秒要處理幾千個read就可以透過複數的node來達成
參考資料
Sharding (Horizontal Scaling)
- 為了提高mongo server的效能,可以增加複數個server,這些server不會儲存相同的資料,而是為把資料切割分開儲存,這些資料會透過shard來分配
- query(insert, write, delete等)就必須透過這些server(或正確的server),每個server管理不同的資料部位
- 對mongo server來說,每個server都會有自己的shard,這些shard會是彼此的副本,這些server和client中間還有一個角色稱為mongos(Router)
- 這個mongos有責任把inserts, read等operation發送給正確的shard,資料必須存在哪個server以及那些server必須提供查找的資料
- 為了達成任務必須透過所謂的shard key,shard key存在於所有document,可以把他是為document的其中一個field,server就可以透過他知道該document是屬於誰
- 使用find找資料時mongos有兩個選項能找到資料
- 如果使用的條件跟不包含shard key,mongos就會向所有的server廣播,讓server交出需要的資料,並把這些資料組合起來再提供給client
- 或是直接使用的條件包含shard key,mongos就可以直接跟負責的server索取資料
- 所以如果有些find是要被頻繁執行的話,必須要有良好的設計,讓shard key被包含在搜尋的條件裡來提供效率
參考資料
Deploy mongoDB server
- 要把mongodb部屬到雲端上而不是在本地端,必須要管理很多任務。必須管理shard, replica set, secure user, encryption, regular backup protect web server/network, update software等複雜的任務
- 對大部分的開發者來說這些任務超出了他們的能力範圍,這時候可以透過Atlas來幫我們輕鬆達成上述的任務
Transaction
- transaction必須要有replica set且mongo 4.0以上才可以運作
- 假設有兩個collections user和post,user可以撰寫post,所以post會有一個field對應到是哪個user撰寫的(1對多),如果我們把其中一個user刪除了,他所建立的post應該也要一併刪除,但是可能會有user成功刪除了,但post沒有順利全部刪除,而transaction可以幫助我們解決這個問題
- 有關連的這些document要不是一起成功刪除,不然就一起刪除失敗
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17
| use blog db.users.insertOne({_id: 1,user: "Max"}) db.post.insertMany([{title: "First post", userId: 1}, {title: "Second post", userId: 1}])
const session = db.getMongo().startSession()
const postsColl = session.getDatabase("blog").posts const usersColl = session.getDatabase("blog").users session.startTransaction() usersColl.deleteOne({_id: 1}) postsColl.deleteMany({userId: 1}) session.commitTransaction()
session.abortTransaction()
|
Transaction參考資料
From shell to driver
- 有些任務必須分工合作,driver適合處理跟應用程式有關的邏輯處理
- shell: Configure Database, Create Collection, Create Indexes
- driver: CRUD operations, Aggregation Pipelines
使用Node.js連接Mongodb範例
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34
| const mongodb = require('mongodb');
const MongoClient = mongodb.MongoClient; const mongoDbUrl = 'mongodb+srv://maximilian:hqG9VedJmagiKhKo@cluster0-ntrwp.mongodb.net/shop?retryWrites=true';
let _db;
const initDb = callback => { if (_db) { console.log('Database is already initialized!'); return callback(null, _db); } MongoClient.connect(mongoDbUrl) .then(client => { _db = client; callback(null, _db); }) .catch(err => { callback(err); }); };
const getDb = () => { if (!_db) { throw Error('Database not initialzed'); } return _db; };
module.exports = { initDb, getDb };
|
- 藉由此段程式碼就可以在其他檔案連接資料庫而不重複與mongodb連線的程式碼
- 透過共用同一個連線,由於driver預設提供的connection pooling,可以同時處理多個要求
透過driver來操作mongoDB 文件