Οι διεπαφές προγραμματισμού εφαρμογών (API) είναι παντού. Επιτρέπουν στο λογισμικό να επικοινωνεί με άλλα κομμάτια λογισμικού - εσωτερικά ή εξωτερικά - με συνέπεια, το οποίο αποτελεί βασικό συστατικό της επεκτασιμότητας, για να μην αναφέρουμε την επαναχρησιμοποίηση.
Είναι πολύ συνηθισμένο σήμερα οι διαδικτυακές υπηρεσίες να διαθέτουν API που βλέπουν στο κοινό. Αυτά επιτρέπουν σε άλλους προγραμματιστές να ενσωματώνουν εύκολα λειτουργίες όπως συνδέσεις κοινωνικών μέσων, πληρωμές με πιστωτική κάρτα και παρακολούθηση συμπεριφοράς. ο στην πραγματικότητα Πρότυπο που χρησιμοποιούν για αυτό ονομάζεται REpresentational State Transfer (REST).
Ενώ μια πληθώρα πλατφορμών και γλωσσών προγραμματισμού μπορούν να χρησιμοποιηθούν για την εργασία - π.χ. ASP.NET Core , Laravel (PHP) , ή Μπουκάλι (Python) - σε αυτό το σεμινάριο, θα δημιουργήσουμε ένα βασικό αλλά ασφαλές REST API back end χρησιμοποιώντας την ακόλουθη στοίβα:
Οι προγραμματιστές που ακολουθούν αυτό το σεμινάριο θα πρέπει επίσης να είναι άνετοι με το τερματικό (ή τη γραμμή εντολών).
Σημείωση: Δεν θα καλύψουμε εδώ μια βάση κώδικα front-end, αλλά το γεγονός ότι το back-end είναι γραμμένο σε JavaScript καθιστά βολικό να μοιράζεστε κώδικα - μοντέλα αντικειμένων, για παράδειγμα - σε ολόκληρη τη στοίβα.
Τα REST API χρησιμοποιούνται για πρόσβαση και χειρισμό δεδομένων χρησιμοποιώντας ένα κοινό σύνολο λειτουργιών χωρίς κατάσταση. Αυτές οι λειτουργίες είναι αναπόσπαστες στο πρωτόκολλο HTTP και αντιπροσωπεύουν βασικές λειτουργίες δημιουργίας, ανάγνωσης, ενημέρωσης και διαγραφής (CRUD), αν και όχι με καθαρό τρόπο ένας προς έναν:
POST
(δημιουργία πόρου ή γενικά παροχή δεδομένων)GET
(ανάκτηση ευρετηρίου πόρων ή μεμονωμένου πόρου)PUT
(δημιουργία ή αντικατάσταση πόρου)PATCH
(ενημέρωση / τροποποίηση ενός πόρου)DELETE
(αφαιρέστε έναν πόρο)Χρησιμοποιώντας αυτές τις λειτουργίες HTTP και ένα όνομα πόρου ως διεύθυνση, μπορούμε να δημιουργήσουμε ένα REST API δημιουργώντας ένα τελικό σημείο για κάθε λειτουργία. Και εφαρμόζοντας το μοτίβο, θα έχουμε ένα σταθερό και εύκολα κατανοητό θεμέλιο που θα μας επιτρέπει να εξελίσσουμε γρήγορα τον κώδικα και να τον διατηρήσουμε μετά. Όπως αναφέρθηκε προηγουμένως, το ίδιο θεμέλιο θα χρησιμοποιηθεί για την ενσωμάτωση λειτουργιών τρίτων, τα περισσότερα από τα οποία χρησιμοποιούν επίσης REST APIs, καθιστώντας την εν λόγω ενσωμάτωση πιο γρήγορη.
Προς το παρόν, ας αρχίσουμε να δημιουργούμε το ασφαλές REST API χρησιμοποιώντας το Node.js!
Σε αυτό το σεμινάριο, θα δημιουργήσουμε ένα αρκετά κοινό (και πολύ πρακτικό) REST API για έναν πόρο που ονομάζεται users
.
Ο πόρος μας θα έχει την ακόλουθη βασική δομή:
id
(ένα UUID που δημιουργήθηκε αυτόματα)firstName
lastName
email
password
permissionLevel
(τι επιτρέπεται να κάνει αυτός ο χρήστης;)Και θα δημιουργήσουμε τις ακόλουθες λειτουργίες για αυτόν τον πόρο:
POST
στο τελικό σημείο /users
(δημιουργία νέου χρήστη)GET
στο τελικό σημείο /users
(λίστα όλων των χρηστών)GET
στο τελικό σημείο /users/:userId
(πάρτε έναν συγκεκριμένο χρήστη)PATCH
στο τελικό σημείο /users/:userId
(ενημέρωση των δεδομένων για έναν συγκεκριμένο χρήστη)DELETE
στο τελικό σημείο /users/:userId
(κατάργηση συγκεκριμένου χρήστη)Θα χρησιμοποιήσουμε επίσης διακριτικά ιστού JSON (JWTs) για διακριτικά πρόσβασης. Για το σκοπό αυτό, θα δημιουργήσουμε έναν άλλο πόρο που ονομάζεται auth
που θα περιμένουν το email και τον κωδικό πρόσβασης ενός χρήστη και, σε αντάλλαγμα, θα δημιουργήσουν το διακριτικό που χρησιμοποιείται για έλεγχο ταυτότητας σε ορισμένες λειτουργίες. (Το υπέροχο άρθρο του Dejan Milosevic για JWT για ασφαλείς εφαρμογές REST στην Java πηγαίνει σε περισσότερες λεπτομέρειες σχετικά με αυτό? οι αρχές είναι οι ίδιες.)
Πρώτα απ 'όλα, βεβαιωθείτε ότι έχετε εγκαταστήσει την τελευταία έκδοση Node.js. Για αυτό το άρθρο, θα χρησιμοποιώ την έκδοση 14.9.0. Μπορεί επίσης να λειτουργεί σε παλαιότερες εκδόσεις.
Στη συνέχεια, βεβαιωθείτε ότι έχετε MongoDB εγκατεστημένο. Δεν θα εξηγήσουμε τις ιδιαιτερότητες του Mongoose και του MongoDB που χρησιμοποιούνται εδώ, αλλά για να λειτουργήσουν τα βασικά, απλώς ξεκινήστε τον διακομιστή σε διαδραστική λειτουργία (δηλαδή, από τη γραμμή εντολών ως mongo
) και όχι ως υπηρεσία. Αυτό συμβαίνει επειδή, σε ένα σημείο σε αυτό το σεμινάριο, θα πρέπει να αλληλεπιδράσουμε με το MongoDB απευθείας και όχι μέσω του κώδικα Node.js.
Σημείωση: Με το MongoDB, δεν χρειάζεται να δημιουργήσετε μια συγκεκριμένη βάση δεδομένων, όπως μπορεί να υπάρχει σε ορισμένα σενάρια RDBMS. Η πρώτη εισαγωγή κλήσης από τον κώδικα Node.js θα ενεργοποιήσει τη δημιουργία της αυτόματα.
Αυτό το σεμινάριο δεν περιέχει όλο τον απαραίτητο κώδικα για ένα έργο που λειτουργεί. Αντ 'αυτού προορίζεται να κλωνοποιήσετε το συνοδευτικό ρεπό και απλώς ακολουθήστε τις καλύτερες στιγμές καθώς διαβάζετε - αλλά μπορείτε επίσης να αντιγράψετε σε συγκεκριμένα αρχεία και αποσπάσματα από το repo, όπως απαιτείται, αν προτιμάτε.
Μεταβείτε στο προκύπτον rest-api-tutorial/
φάκελο στο τερματικό σας. Θα δείτε ότι το έργο μας περιέχει τρεις φακέλους λειτουργικών μονάδων:
common
(χειρισμός όλων των κοινών υπηρεσιών και πληροφορίες που μοιράζονται μεταξύ των ενοτήτων χρήστη)users
(τα πάντα σχετικά με τους χρήστες)auth
(χειρισμός της παραγωγής JWT και της ροής σύνδεσης)Τώρα, εκτελέστε npm install
(ή yarn
εάν το έχετε.)
Συγχαρητήρια, έχετε πλέον όλες τις εξαρτήσεις και τη ρύθμιση που απαιτούνται για την εκτέλεση του απλού REST API.
Θα χρησιμοποιούμε Μαγκούστα , μια βιβλιοθήκη μοντελοποίησης δεδομένων αντικειμένων (ODM) για το MongoDB, για τη δημιουργία του μοντέλου χρήστη στο σχήμα χρήστη.
Πρώτον, πρέπει να δημιουργήσουμε το σχήμα Mongoose στο /users/models/users.model.js
:
const userSchema = new Schema({ firstName: String, lastName: String, email: String, password: String, permissionLevel: Number });
Μόλις ορίσουμε το σχήμα, μπορούμε εύκολα να προσαρτήσουμε το σχήμα στο μοντέλο χρήστη.
const userModel = mongoose.model('Users', userSchema);
Μετά από αυτό, μπορούμε να χρησιμοποιήσουμε αυτό το μοντέλο για να εφαρμόσουμε όλες τις λειτουργίες CRUD που θέλουμε στα τελικά σημεία Express.
τρέξτε σε ui thread android
Ας ξεκινήσουμε με τη λειτουργία 'δημιουργία χρήστη' καθορίζοντας τη διαδρομή σε users/routes.config.js
:
app.post('/users', [ UsersController.insert ]);
Αυτό μεταφέρεται στην εφαρμογή Express στην κύρια index.js
αρχείο. Το UsersController
το αντικείμενο εισάγεται από τον ελεγκτή μας, όπου έχουμε κατακερματιστεί τον κωδικό πρόσβασης κατάλληλα, όπως ορίζεται στο /users/controllers/users.controller.js
:
exports.insert = (req, res) => { let salt = crypto.randomBytes(16).toString('base64'); let hash = crypto.createHmac('sha512',salt) .update(req.body.password) .digest('base64'); req.body.password = salt + '$' + hash; req.body.permissionLevel = 1; UserModel.createUser(req.body) .then((result) => { res.status(201).send({id: result._id}); }); };
Σε αυτό το σημείο, μπορούμε να δοκιμάσουμε το μοντέλο Mongoose εκτελώντας τον διακομιστή (npm start
) και στέλνοντας ένα POST
αίτημα προς /users
με ορισμένα δεδομένα JSON:
{ 'firstName' : 'Marcos', 'lastName' : 'Silva', 'email' : ' [email protected] ', 'password' : 's3cr3tp4sswo4rd' }
Υπάρχουν πολλά εργαλεία που μπορείτε να χρησιμοποιήσετε για αυτό. Η αϋπνία (καλύπτεται παρακάτω) και ο Ταχυδρόμος είναι δημοφιλή εργαλεία GUI και curl
είναι μια κοινή επιλογή CLI. Μπορείτε ακόμη και να χρησιμοποιήσετε τη JavaScript, π.χ. από την ενσωματωμένη κονσόλα εργαλείων ανάπτυξης του προγράμματος περιήγησής σας:
fetch('http://localhost:3600/users', { method: 'POST', headers: { 'Content-type': 'application/json' }, body: JSON.stringify({ 'firstName': 'Marcos', 'lastName': 'Silva', 'email': ' [email protected] ', 'password': 's3cr3tp4sswo4rd' }) }) .then(function(response) { return response.json(); }) .then(function(data) { console.log('Request succeeded with JSON response', data); }) .catch(function(error) { console.log('Request failed', error); });
Σε αυτό το σημείο, το αποτέλεσμα μιας έγκυρης ανάρτησης θα είναι μόνο το αναγνωριστικό από τον δημιουργημένο χρήστη: { 'id': '5b02c5c84817bf28049e58a3' }
. Πρέπει επίσης να προσθέσουμε το createUser
μέθοδος στο μοντέλο σε users/models/users.model.js
:
exports.createUser = (userData) => { const user = new User(userData); return user.save(); };
Όλα έτοιμα, τώρα πρέπει να δούμε αν ο χρήστης υπάρχει. Γι 'αυτό, θα εφαρμόσουμε τη λειτουργία 'get user by id' για το ακόλουθο τελικό σημείο: users/:userId
.
Αρχικά, δημιουργούμε μια διαδρομή σε /users/routes/config.js
:
app.get('/users/:userId', [ UsersController.getById ]);
Στη συνέχεια, δημιουργούμε τον ελεγκτή στο /users/controllers/users.controller.js
:
exports.getById = (req, res) => { UserModel.findById(req.params.userId).then((result) => { res.status(200).send(result); }); };
Και τέλος, προσθέστε το findById
μέθοδος στο μοντέλο σε /users/models/users.model.js
:
exports.findById = (id) => { return User.findById(id).then((result) => { result = result.toJSON(); delete result._id; delete result.__v; return result; }); };
Η απάντηση θα είναι η εξής:
{ 'firstName': 'Marcos', 'lastName': 'Silva', 'email': ' [email protected] ', 'password': 'Y+XZEaR7J8xAQCc37nf1rw==$p8b5ykUx6xpC6k8MryDaRmXDxncLumU9mEVabyLdpotO66Qjh0igVOVerdqAh+CUQ4n/E0z48mp8SDTpX2ivuQ==', 'permissionLevel': 1, 'id': '5b02c5c84817bf28049e58a3' }
Σημειώστε ότι μπορούμε να δούμε τον κατακερματισμένο κωδικό πρόσβασης. Για αυτό το σεμινάριο, παρουσιάζουμε τον κωδικό πρόσβασης, αλλά η προφανής βέλτιστη πρακτική δεν είναι ποτέ να αποκαλύπτετε τον κωδικό πρόσβασης, ακόμα κι αν έχει κατακερματιστεί. Ένα άλλο πράγμα που μπορούμε να δούμε είναι το permissionLevel
, το οποίο θα χρησιμοποιήσουμε για να χειριστούμε τα δικαιώματα χρήστη αργότερα.
Επαναλαμβάνοντας το μοτίβο που περιγράφεται παραπάνω, μπορούμε πλέον να προσθέσουμε τη λειτουργικότητα για ενημέρωση του χρήστη. Θα χρησιμοποιήσουμε το PATCH
λειτουργία δεδομένου ότι θα μας επιτρέψει να στείλουμε μόνο τα πεδία που θέλουμε να αλλάξουμε. Επομένως, η διαδρομή θα είναι PATCH
στο /users/:userid
και θα στέλνουμε τα πεδία που θέλουμε να αλλάξουμε. Θα χρειαστεί επίσης να εφαρμόσουμε κάποια επιπλέον επικύρωση, καθώς οι αλλαγές πρέπει να περιορίζονται στον εν λόγω χρήστη ή έναν διαχειριστή και μόνο ένας διαχειριστής θα πρέπει να μπορεί να αλλάξει το permissionLevel
. Θα το παραλείψουμε προς το παρόν και θα το επιστρέψουμε μόλις εφαρμόσουμε τη μονάδα ελέγχου. Προς το παρόν, ο ελεγκτής μας θα μοιάζει με αυτό:
exports.patchById = (req, res) => { if (req.body.password){ let salt = crypto.randomBytes(16).toString('base64'); let hash = crypto.createHmac('sha512', salt).update(req.body.password).digest('base64'); req.body.password = salt + '$' + hash; } UserModel.patchUser(req.params.userId, req.body).then((result) => { res.status(204).send({}); }); };
Από προεπιλογή, θα στείλουμε έναν κωδικό HTTP 204 χωρίς σώμα απόκρισης για να δείξουμε ότι το αίτημα ήταν επιτυχές.
Και θα πρέπει να προσθέσουμε το patchUser
μέθοδος στο μοντέλο:
exports.patchUser = (id, userData) => { return User.findOneAndUpdate({ _id: id }, userData); };
Η λίστα χρηστών θα εφαρμοστεί ως GET
στο /users/
από τον ακόλουθο ελεγκτή:
exports.list = (req, res) => { let limit = req.query.limit && req.query.limit { res.status(200).send(result); }) };
Η αντίστοιχη μέθοδος μοντέλου θα είναι:
exports.list = (perPage, page) => { return new Promise((resolve, reject) => { User.find() .limit(perPage) .skip(perPage * page) .exec(function (err, users) { if (err) { reject(err); } else { resolve(users); } }) }); };
Η προκύπτουσα απόκριση λίστας θα έχει την ακόλουθη δομή:
[ { 'firstName': 'Marco', 'lastName': 'Silva', 'email': ' [email protected] ', 'password': 'z4tS/DtiH+0Gb4J6QN1K3w==$al6sGxKBKqxRQkDmhnhQpEB6+DQgDRH2qr47BZcqLm4/fphZ7+a9U+HhxsNaSnGB2l05Oem/BLIOkbtOuw1tXA==', 'permissionLevel': 1, 'id': '5b02c5c84817bf28049e58a3' }, { 'firstName': 'Paulo', 'lastName': 'Silva', 'email': ' [email protected] ', 'password': 'wTsqO1kHuVisfDIcgl5YmQ==$cw7RntNrNBNw3MO2qLbx959xDvvrDu4xjpYfYgYMxRVDcxUUEgulTlNSBJjiDtJ1C85YimkMlYruU59rx2zbCw==', 'permissionLevel': 1, 'id': '5b02d038b653603d1ca69729' } ]
Και το τελευταίο μέρος που θα εφαρμοστεί είναι το DELETE
στο /users/:userId
.
Ο ελεγκτής μας για διαγραφή θα είναι:
exports.removeById = (req, res) => { UserModel.removeById(req.params.userId) .then((result)=>{ res.status(204).send({}); }); };
Όπως και πριν, ο ελεγκτής θα επιστρέψει τον κωδικό HTTP 204 και κανένα σώμα περιεχομένου ως επιβεβαίωση.
Η αντίστοιχη μέθοδος μοντέλου πρέπει να έχει την εξής μορφή:
exports.removeById = (userId) => { return new Promise((resolve, reject) => { User.deleteMany({_id: userId}, (err) => { if (err) { reject(err); } else { resolve(err); } }); }); };
Έχουμε πλέον όλες τις απαραίτητες λειτουργίες για το χειρισμό του πόρου χρήστη και τελειώσαμε με τον ελεγκτή χρήστη Η κύρια ιδέα αυτού του κώδικα είναι να σας δώσει τις βασικές έννοιες της χρήσης του μοτίβου REST. Θα πρέπει να επιστρέψουμε σε αυτόν τον κώδικα για να εφαρμόσουμε ορισμένες επικυρώσεις και δικαιώματα σε αυτόν, αλλά πρώτα, θα πρέπει να αρχίσουμε να δημιουργούμε την ασφάλειά μας. Ας δημιουργήσουμε τη λειτουργική μονάδα.
Πριν μπορέσουμε να διασφαλίσουμε το users
ενότητα με την εφαρμογή του μέσου άδειας και επικύρωσης, θα πρέπει να είμαστε σε θέση να δημιουργήσουμε ένα έγκυρο διακριτικό για τον τρέχοντα χρήστη. Θα δημιουργήσουμε ένα JWT ως απάντηση στο χρήστη που θα παρέχει έγκυρα email και κωδικό πρόσβασης. Το JWT είναι ένα αξιοσημείωτο διακριτικό ιστού JSON που μπορείτε να χρησιμοποιήσετε για να κάνετε ο χρήστης να υποβάλει με ασφάλεια πολλά αιτήματα χωρίς να επαληθεύει επανειλημμένα. Συνήθως έχει χρόνο λήξης και δημιουργείται ένα νέο διακριτικό κάθε λίγα λεπτά για να διατηρείται η επικοινωνία ασφαλής. Ωστόσο, για αυτό το σεμινάριο, θα ξεχάσουμε την ανανέωση του διακριτικού και θα το κρατήσουμε απλό με ένα διακριτικό ανά σύνδεση.
Αρχικά, θα δημιουργήσουμε ένα τελικό σημείο για το POST
αιτήματα για /auth
πόρος. Το σώμα του αιτήματος θα περιέχει το email χρήστη και τον κωδικό πρόσβασης:
{ 'email' : ' [email protected] ', 'password' : 's3cr3tp4sswo4rd2' }
Πριν δεσμεύσουμε τον ελεγκτή, πρέπει να επικυρώσουμε τον χρήστη στο /authorization/middlewares/verify.user.middleware.js
:
exports.isPasswordAndUserMatch = (req, res, next) => { UserModel.findByEmail(req.body.email) .then((user)=>{ if(!user[0]){ res.status(404).send({}); }else{ let passwordFields = user[0].password.split('$'); let salt = passwordFields[0]; let hash = crypto.createHmac('sha512', salt).update(req.body.password).digest('base64'); if (hash === passwordFields[1]) { req.body = { userId: user[0]._id, email: user[0].email, permissionLevel: user[0].permissionLevel, provider: 'email', name: user[0].firstName + ' ' + user[0].lastName, }; return next(); } else { return res.status(400).send({errors: ['Invalid email or password']}); } } }); };
Μετά από αυτό, μπορούμε να προχωρήσουμε στον ελεγκτή και να δημιουργήσουμε το JWT:
exports.login = (req, res) => { try { let refreshId = req.body.userId + jwtSecret; let salt = crypto.randomBytes(16).toString('base64'); let hash = crypto.createHmac('sha512', salt).update(refreshId).digest('base64'); req.body.refreshKey = salt; let token = jwt.sign(req.body, jwtSecret); let b = Buffer.from(hash); let refresh_token = b.toString('base64'); res.status(201).send({accessToken: token, refreshToken: refresh_token}); } catch (err) { res.status(500).send({errors: err}); } };
Παρόλο που δεν θα ανανεώσουμε το διακριτικό σε αυτό το σεμινάριο, ο ελεγκτής έχει ρυθμιστεί για να επιτρέψει σε αυτήν τη γενιά να διευκολύνει την εφαρμογή του στην επόμενη ανάπτυξη.
Το μόνο που χρειαζόμαστε τώρα είναι να δημιουργήσουμε τη διαδρομή και να καλέσουμε το κατάλληλο μεσαίο λογισμικό στο /authorization/routes.config.js
:
app.post('/auth', [ VerifyUserMiddleware.hasAuthValidFields, VerifyUserMiddleware.isPasswordAndUserMatch, AuthorizationController.login ]);
Η απόκριση θα περιέχει το δημιουργημένο JWT στο πεδίο accessToken:
{ 'accessToken': 'eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJ1c2VySWQiOiI1YjAyYzVjODQ4MTdiZjI4MDQ5ZTU4YTMiLCJlbWFpbCI6Im1hcmNvcy5oZW5yaXF1ZUB0b3B0YWwuY29tIiwicGVybWlzc2lvbkxldmVsIjoxLCJwcm92aWRlciI6ImVtYWlsIiwibmFtZSI6Ik1hcmNvIFNpbHZhIiwicmVmcmVzaF9rZXkiOiJiclhZUHFsbUlBcE1PakZIRG1FeENRPT0iLCJpYXQiOjE1MjY5MjMzMDl9.mmNg-i44VQlUEWP3YIAYXVO-74803v1mu-y9QPUQ5VY', 'refreshToken': 'U3BDQXBWS3kyaHNDaGJNanlJTlFkSXhLMmFHMzA2NzRsUy9Sd2J0YVNDTmUva0pIQ0NwbTJqOU5YZHgxeE12NXVlOUhnMzBWMGNyWmdOTUhSaTdyOGc9PQ==' }
Αφού δημιουργήσαμε το διακριτικό, μπορούμε να το χρησιμοποιήσουμε μέσα στο Authorization
κεφαλίδα χρησιμοποιώντας τη φόρμα Bearer ACCESS_TOKEN
.
Το πρώτο πράγμα που πρέπει να ορίσουμε είναι ποιος μπορεί να χρησιμοποιήσει το users
πόρος. Αυτά είναι τα σενάρια που θα πρέπει να χειριστούμε:
Έχοντας εντοπίσει αυτά τα σενάρια, θα χρειαζόμαστε πρώτα ένα μεσαίο λογισμικό που επικυρώνει πάντα τον χρήστη εάν χρησιμοποιεί ένα έγκυρο JWT. Το μεσαίο λογισμικό σε /common/middlewares/auth.validation.middleware.js
μπορεί να είναι τόσο απλό όσο:
πώς να γράψετε έναν κωδικό
exports.validJWTNeeded = (req, res, next) => { if (req.headers['authorization']) { try { let authorization = req.headers['authorization'].split(' '); if (authorization[0] !== 'Bearer') { return res.status(401).send(); } else { req.jwt = jwt.verify(authorization[1], secret); return next(); } } catch (err) { return res.status(403).send(); } } else { return res.status(401).send(); } };
Θα χρησιμοποιήσουμε κωδικούς σφάλματος HTTP για τον χειρισμό σφαλμάτων αιτήματος:
Μπορούμε να χρησιμοποιήσουμε τον τελεστή bitwise AND (bitmasking) για τον έλεγχο των δικαιωμάτων. Εάν ορίσουμε κάθε απαιτούμενη άδεια ως ισχύ 2, μπορούμε να αντιμετωπίσουμε κάθε bit του ακέραιου 32-bit ως ένα μόνο δικαίωμα. Στη συνέχεια, ένας διαχειριστής μπορεί να έχει όλα τα δικαιώματα ορίζοντας την τιμή άδειας σε 2147483647. Αυτός ο χρήστης θα μπορούσε τότε να έχει πρόσβαση σε οποιαδήποτε διαδρομή. Ως άλλο παράδειγμα, ένας χρήστης του οποίου η τιμή δικαιωμάτων ορίστηκε σε 7 θα έχει δικαιώματα για τους ρόλους που επισημαίνονται με bit για τις τιμές 1, 2 και 4 (δύο με ισχύ 0, 1 και 2).
Το μεσαίο λογισμικό για αυτό θα μοιάζει με αυτό:
exports.minimumPermissionLevelRequired = (required_permission_level) => { return (req, res, next) => { let user_permission_level = parseInt(req.jwt.permission_level); let user_id = req.jwt.user_id; if (user_permission_level & required_permission_level) { return next(); } else { return res.status(403).send(); } }; };
Το μεσαίο λογισμικό είναι γενικό. Εάν το επίπεδο άδειας χρήστη και το απαιτούμενο επίπεδο άδειας συμπίπτουν σε τουλάχιστον ένα bit, το αποτέλεσμα θα είναι μεγαλύτερο από το μηδέν και μπορούμε να αφήσουμε την ενέργεια να προχωρήσει. Διαφορετικά, ο κωδικός HTTP 403 θα επιστραφεί.
Τώρα, πρέπει να προσθέσουμε το ενδιάμεσο λογισμικό ελέγχου ταυτότητας στις διαδρομές της λειτουργικής μονάδας του χρήστη στο /users/routes.config.js
:
app.post('/users', [ UsersController.insert ]); app.get('/users', [ ValidationMiddleware.validJWTNeeded, PermissionMiddleware.minimumPermissionLevelRequired(PAID), UsersController.list ]); app.get('/users/:userId', [ ValidationMiddleware.validJWTNeeded, PermissionMiddleware.minimumPermissionLevelRequired(FREE), PermissionMiddleware.onlySameUserOrAdminCanDoThisAction, UsersController.getById ]); app.patch('/users/:userId', [ ValidationMiddleware.validJWTNeeded, PermissionMiddleware.minimumPermissionLevelRequired(FREE), PermissionMiddleware.onlySameUserOrAdminCanDoThisAction, UsersController.patchById ]); app.delete('/users/:userId', [ ValidationMiddleware.validJWTNeeded, PermissionMiddleware.minimumPermissionLevelRequired(ADMIN), UsersController.removeById ]);
Αυτό ολοκληρώνει τη βασική ανάπτυξη του REST API μας. Το μόνο που μένει να κάνουμε είναι να το δοκιμάσουμε όλα.
Αυπνία είναι ένας αξιοπρεπής πελάτης REST με μια καλή δωρεάν έκδοση. Η βέλτιστη πρακτική είναι, φυσικά, να συμπεριλάβετε δοκιμές κώδικα και να εφαρμόσετε σωστή αναφορά σφαλμάτων στο έργο, αλλά οι πελάτες REST τρίτων είναι ιδανικοί για τη δοκιμή και την εφαρμογή λύσεων τρίτων όταν η αναφορά σφαλμάτων και ο εντοπισμός σφαλμάτων δεν είναι διαθέσιμη. Θα το χρησιμοποιήσουμε εδώ για να παίξουμε το ρόλο μιας εφαρμογής και να μάθουμε τι συμβαίνει με το API μας.
Για να δημιουργήσουμε έναν χρήστη, πρέπει απλώς να POST
τα απαιτούμενα πεδία στο κατάλληλο τελικό σημείο και αποθηκεύστε το δημιουργημένο αναγνωριστικό για μελλοντική χρήση.
Το API θα ανταποκριθεί με το αναγνωριστικό χρήστη:
Μπορούμε τώρα να δημιουργήσουμε το JWT χρησιμοποιώντας το /auth/
τελικό σημείο:
Πρέπει να λάβουμε ένα σύμβολο ως απάντηση μας:
Πιάστε το accessToken
, προθέστε το με Bearer
(θυμηθείτε το κενό διάστημα) και προσθέστε το στις κεφαλίδες αιτήματος κάτω από το Authorization
:
Εάν δεν το κάνουμε τώρα που έχουμε εφαρμόσει το ενδιάμεσο λογισμικό αδειών, κάθε αίτημα εκτός από την εγγραφή θα επιστρέφει τον κωδικό HTTP 401. Ωστόσο, με το έγκυρο διακριτικό, λαμβάνουμε την ακόλουθη απάντηση από το /users/:userId
:
Επίσης, όπως αναφέρθηκε προηγουμένως, προβάλλουμε όλα τα πεδία, για εκπαιδευτικούς σκοπούς και για λόγους απλότητας. Ο κωδικός πρόσβασης (κατακερματισμός ή άλλως) δεν πρέπει ποτέ να είναι ορατός στην απόκριση.
Ας προσπαθήσουμε να λάβουμε μια λίστα χρηστών:
Εκπληξη! Λαμβάνουμε 403 απάντηση.
Ο χρήστης μας δεν έχει τα δικαιώματα πρόσβασης σε αυτό το τελικό σημείο. Θα πρέπει να αλλάξουμε το permissionLevel
του χρήστη μας από το 1 έως το 7 (ή ακόμα και 5 θα το έκαναν, δεδομένου ότι τα επίπεδα δωρεάν και πληρωμένων δικαιωμάτων μας αντιπροσωπεύονται ως 1 και 4, αντίστοιχα.) Μπορούμε να το κάνουμε χειροκίνητα στο MongoDB, στη διαδραστική προτροπή του, όπως αυτό (με το αναγνωριστικό άλλαξε στο τοπικό αποτέλεσμα):
db.users.update({'_id' : ObjectId('5b02c5c84817bf28049e58a3')},{$set:{'permissionLevel':5}})
Στη συνέχεια, πρέπει να δημιουργήσουμε ένα νέο JWT.
Αφού γίνει αυτό, λαμβάνουμε τη σωστή απάντηση:
Στη συνέχεια, ας δοκιμάσουμε τη λειτουργικότητα ενημέρωσης στέλνοντας ένα PATCH
αίτημα με ορισμένα πεδία στο /users/:userId
τελικό σημείο:
Αναμένουμε μια απάντηση 204 ως επιβεβαίωση μιας επιτυχούς λειτουργίας, αλλά μπορούμε να ζητήσουμε από τον χρήστη να επιβεβαιώσει ξανά.
Τέλος, πρέπει να διαγράψουμε τον χρήστη. Θα πρέπει να δημιουργήσουμε έναν νέο χρήστη όπως περιγράφεται παραπάνω (μην ξεχάσετε να σημειώσετε το αναγνωριστικό χρήστη) και βεβαιωθείτε ότι διαθέτουμε το κατάλληλο JWT για έναν διαχειριστή. Ο νέος χρήστης θα χρειαστεί τα δικαιώματα που έχει οριστεί σε 2053 (δηλαδή το 2048— ADMIN
—επάνω από τα προηγούμενα 5) για να μπορεί επίσης να εκτελέσει τη λειτουργία διαγραφής. Με αυτό που έχει γίνει και δημιουργηθεί ένα νέο JWT, θα πρέπει να ενημερώσουμε το Authorization
κεφαλίδα αιτήματος:
Αποστολή a DELETE
αίτημα προς /users/:userId
, θα πρέπει να λάβουμε μια απάντηση 204 ως επιβεβαίωση. Μπορούμε, πάλι, να επαληθεύσουμε ζητώντας /users/
για να απαριθμήσετε όλους τους υπάρχοντες χρήστες.
Με τα εργαλεία και τις μεθόδους που καλύπτονται σε αυτό το σεμινάριο, θα πρέπει τώρα να μπορείτε να δημιουργήσετε απλά και ασφαλή REST API στο Node.js. Παραλείφθηκαν πολλές βέλτιστες πρακτικές που δεν είναι απαραίτητες για τη διαδικασία, οπότε μην ξεχάσετε να:
common/config/env.config.js
σε off-repo, χωρίς περιβάλλον μηχανισμός μυστικής διανομής Μια τελική άσκηση για τον αναγνώστη μπορεί να είναι να μετατρέψει τη βάση κώδικα από τη χρήση των υποσχέσεων JavaScript σε async / περιμένετε τεχνική.
Για όσους από εσάς μπορεί να σας ενδιαφέρουν, υπάρχει και τώρα μια έκδοση TypeScript του διαθέσιμου έργου.
Σχετίζεται με: 5 πράγματα που δεν έχετε κάνει ποτέ με μια προδιαγραφή REST