Το GraphQL είναι μια δημοφιλής εναλλακτική λύση στην παραδοσιακή αρχιτεκτονική RESTful API, προσφέροντας μια ευέλικτη και αποτελεσματική γλώσσα αναζήτησης δεδομένων και χειρισμού για API. Με τα αυξανόμενη υιοθέτηση, καθίσταται ολοένα και πιο σημαντικό να δίνεται προτεραιότητα στην ασφάλεια των GraphQL API για την προστασία των εφαρμογών από μη εξουσιοδοτημένη πρόσβαση και πιθανά δεδομένα παραβιάσεις.
Μια αποτελεσματική προσέγγιση για την ασφάλεια των GraphQL API είναι η εφαρμογή JSON Web Tokens (JWT). Τα JWT παρέχουν μια ασφαλή και αποτελεσματική μέθοδο για την παροχή πρόσβασης σε προστατευμένους πόρους και την εκτέλεση εξουσιοδοτημένων ενεργειών, διασφαλίζοντας την ασφαλή επικοινωνία μεταξύ των πελατών και των API.
Έλεγχος ταυτότητας και εξουσιοδότηση σε GraphQL API
Διαφορετικός REST API, Τα GraphQL API έχουν συνήθως ένα μόνο τελικό σημείο που επιτρέπει στους πελάτες να ζητούν δυναμικά ποικίλες ποσότητες δεδομένων στα ερωτήματά τους. Αν και αυτή η ευελιξία είναι η δύναμή του, αυξάνει επίσης τον κίνδυνο πιθανών επιθέσεων ασφαλείας, όπως σπασμένα τρωτά σημεία ελέγχου πρόσβασης.
Για να μετριαστεί αυτός ο κίνδυνος, είναι σημαντικό να εφαρμόζονται ισχυρές διαδικασίες ελέγχου ταυτότητας και εξουσιοδότησης, συμπεριλαμβανομένου του κατάλληλου καθορισμού αδειών πρόσβασης. Με αυτόν τον τρόπο, εγγυάστε ότι μόνο εξουσιοδοτημένοι χρήστες μπορούν να έχουν πρόσβαση σε προστατευμένους πόρους και, τελικά, μειώνετε τον κίνδυνο πιθανών παραβιάσεων της ασφάλειας και απώλειας δεδομένων.
Μπορείτε να βρείτε τον κωδικό αυτού του έργου σε αυτό GitHub αποθήκη.
Ρυθμίστε έναν διακομιστή Express.js Apollo
Διακομιστής Apollo είναι μια ευρέως χρησιμοποιούμενη υλοποίηση διακομιστή GraphQL για GraphQL API. Μπορείτε να το χρησιμοποιήσετε για να δημιουργήσετε εύκολα σχήματα GraphQL, να ορίσετε λύσεις επίλυσης και να διαχειριστείτε διαφορετικές πηγές δεδομένων για τα API σας.
Για να ρυθμίσετε έναν διακομιστή Express.js Apollo, δημιουργήστε και ανοίξτε έναν φάκελο έργου:
mkdir graphql-API-jwt
cd graphql-API-jwt
Στη συνέχεια, εκτελέστε αυτήν την εντολή για να αρχικοποιήσετε ένα νέο έργο Node.js χρησιμοποιώντας npm, ο διαχειριστής πακέτων Node:
npm init --yes
Τώρα, εγκαταστήστε αυτά τα πακέτα.
npm install apollo-server graphql mongoose jsonwebtokens dotenv
Τέλος, δημιουργήστε ένα server.js αρχείο στον ριζικό κατάλογο και ρυθμίστε τον διακομιστή σας με αυτόν τον κώδικα:
const { ApolloServer } = require('apollo-server');
const mongoose = require('mongoose');
require('dotenv').config();const typeDefs = require("./graphql/typeDefs");
const resolvers = require("./graphql/resolvers");const server = new ApolloServer({
typeDefs,
resolvers,
context: ({ req }) => ({ req }),
});const MONGO_URI = process.env.MONGO_URI;
mongoose
.connect(MONGO_URI, {
useNewUrlParser: true,
useUnifiedTopology: true,
})
.then(() => {
console.log("Connected to DB");
return server.listen({ port: 5000 });
})
.then((res) => {
console.log(`Server running at ${res.url}`);
})
.catch(err => {
console.log(err.message);
});
Ο διακομιστής GraphQL έχει ρυθμιστεί με το typeDefs και επιλύτες παραμέτρους, προσδιορίζοντας το σχήμα και τις λειτουργίες που μπορεί να χειριστεί το API. ο συμφραζόμενα Η επιλογή διαμορφώνει το αντικείμενο req στο περιβάλλον κάθε προγράμματος επίλυσης, το οποίο θα επιτρέψει στον διακομιστή να έχει πρόσβαση σε λεπτομέρειες που αφορούν το αίτημα, όπως τιμές κεφαλίδας.
Δημιουργήστε μια βάση δεδομένων MongoDB
Για να δημιουργήσετε τη σύνδεση της βάσης δεδομένων, πρώτα δημιουργήστε μια βάση δεδομένων MongoDB ή δημιουργήστε ένα σύμπλεγμα στο MongoDB Atlas. Στη συνέχεια, αντιγράψτε την παρεχόμενη συμβολοσειρά URI σύνδεσης βάσης δεδομένων, δημιουργήστε ένα .env αρχείο και εισαγάγετε τη συμβολοσειρά σύνδεσης ως εξής:
MONGO_URI=""
Ορίστε το μοντέλο δεδομένων
Ορίστε ένα μοντέλο δεδομένων χρησιμοποιώντας το Mongoose. Δημιούργησε ένα νέο models/user.js αρχείο και συμπεριλάβετε τον ακόλουθο κώδικα:
const {model, Schema} = require('mongoose');
const userSchema = new Schema({
name: String,
password: String,
role: String
});
module.exports = model('user', userSchema);
Ορίστε το σχήμα GraphQL
Σε ένα GraphQL API, το σχήμα καθορίζει τη δομή των δεδομένων που μπορούν να ερωτηθούν, καθώς και τις διαθέσιμες λειτουργίες (ερωτήματα και μεταλλάξεις) που μπορείτε να εκτελέσετε για να αλληλεπιδράσετε με δεδομένα μέσω του API.
Για να ορίσετε ένα σχήμα, δημιουργήστε έναν νέο φάκελο στον ριζικό κατάλογο του έργου σας και ονομάστε τον graphql. Μέσα σε αυτόν τον φάκελο, προσθέστε δύο αρχεία: typeDefs.js και επιλύτες.js.
Στο typeDefs.js αρχείο, συμπεριλάβετε τον ακόλουθο κώδικα:
const { gql } = require("apollo-server");
const typeDefs = gql`
type User {
id: ID!
name: String!
password: String!
role: String!
}
input UserInput {
name: String!
password: String!
role: String!
}
type TokenResult {
message: String
token: String
}
type Query {
users: [User]
}
type Mutation {
register(userInput: UserInput): User
login(name: String!, password: String!, role: String!): TokenResult
}
`;
module.exports = typeDefs;
Δημιουργήστε Resolvers για το GraphQL API
Οι συναρτήσεις επίλυσης καθορίζουν τον τρόπο ανάκτησης των δεδομένων ως απόκριση σε ερωτήματα και μεταλλάξεις πελάτη, καθώς και σε άλλα πεδία που ορίζονται στο σχήμα. Όταν ένας πελάτης στέλνει ένα ερώτημα ή μια μετάλλαξη, ο διακομιστής GraphQL ενεργοποιεί τους αντίστοιχους επιλύτες για να επεξεργαστούν και να επιστρέψουν τα απαιτούμενα δεδομένα από διάφορες πηγές, όπως βάσεις δεδομένων ή API.
Για να εφαρμόσετε έλεγχο ταυτότητας και εξουσιοδότηση χρησιμοποιώντας JSON Web Tokens (JWT), ορίστε επιλύτες για μεταλλάξεις μητρώου και σύνδεσης. Αυτά θα χειρίζονται τις διαδικασίες εγγραφής και ελέγχου ταυτότητας χρήστη. Στη συνέχεια, δημιουργήστε ένα πρόγραμμα επίλυσης ερωτημάτων ανάκτησης δεδομένων που θα είναι προσβάσιμο μόνο σε πιστοποιημένους και εξουσιοδοτημένους χρήστες.
Αλλά πρώτα, ορίστε τις συναρτήσεις για τη δημιουργία και την επαλήθευση των JWT. Στο επιλύτες.js αρχείο, ξεκινήστε προσθέτοντας τις ακόλουθες εισαγωγές.
const User = require("../models/user");
const jwt = require('jsonwebtoken');
const secretKey = process.env.SECRET_KEY;
Φροντίστε να προσθέσετε το μυστικό κλειδί που θα χρησιμοποιήσετε για να υπογράψετε JSON web token στο αρχείο .env.
SECRET_KEY = '' ;
Για να δημιουργήσετε ένα διακριτικό ελέγχου ταυτότητας, συμπεριλάβετε την ακόλουθη συνάρτηση, η οποία καθορίζει επίσης μοναδικά χαρακτηριστικά για το διακριτικό JWT, π.χ. τον χρόνο λήξης. Επιπλέον, μπορείτε να ενσωματώσετε και άλλα χαρακτηριστικά, όπως εκδοθέντα κατά τον χρόνο με βάση τις συγκεκριμένες απαιτήσεις της εφαρμογής σας.
functiongenerateToken(user) {
const token = jwt.sign(
{ id: user.id, role: user.role },
secretKey,
{ expiresIn: '1h', algorithm: 'HS256' }
);
return token;
}
Τώρα, εφαρμόστε τη λογική επαλήθευσης διακριτικού για να επικυρώσετε τα διακριτικά JWT που περιλαμβάνονται σε επόμενα αιτήματα HTTP.
functionverifyToken(token) {
if (!token) {
thrownewError('Token not provided');
}
try {
const decoded = jwt.verify(token, secretKey, { algorithms: ['HS256'] });
return decoded;
} catch (err) {
thrownewError('Invalid token');
}
}
Αυτή η συνάρτηση θα λάβει ένα διακριτικό ως είσοδο, θα επαληθεύσει την εγκυρότητά του χρησιμοποιώντας το καθορισμένο μυστικό κλειδί και θα επιστρέψει το αποκωδικοποιημένο διακριτικό εάν είναι έγκυρο, διαφορετικά θα εμφανίσει ένα σφάλμα που υποδεικνύει μη έγκυρο διακριτικό.
Καθορίστε τα API Resolvers
Για να ορίσετε τους επιλύτες για το GraphQL API, πρέπει να περιγράψετε τις συγκεκριμένες λειτουργίες που θα διαχειρίζεται, σε αυτήν την περίπτωση, τις λειτουργίες εγγραφής χρήστη και σύνδεσης. Πρώτα, δημιουργήστε ένα επιλύτες αντικείμενο που θα κρατήσει τις συναρτήσεις του αναλυτή και, στη συνέχεια, ορίστε τις ακόλουθες λειτουργίες μετάλλαξης:
const resolvers = {
Mutation: {
register: async (_, { userInput: { name, password, role } }) => {
if (!name || !password || !role) {
thrownewError('Name password, and role required');
}const newUser = new User({
name: name,
password: password,
role: role,
});try {
const response = await newUser.save();return {
id: response._id,
...response._doc,
};
} catch (error) {
console.error(error);
thrownewError('Failed to create user');
}
},
login: async (_, { name, password }) => {
try {
const user = await User.findOne({ name: name });if (!user) {
thrownewError('User not found');
}if (password !== user.password) {
thrownewError('Incorrect password');
}const token = generateToken(user);
if (!token) {
thrownewError('Failed to generate token');
}
return {
message: 'Login successful',
token: token,
};
} catch (error) {
console.error(error);
thrownewError('Login failed');
}
}
},
ο κανω ΕΓΓΡΑΦΗ Το mutation χειρίζεται τη διαδικασία εγγραφής προσθέτοντας τα νέα δεδομένα χρήστη στη βάση δεδομένων. Ενώ το Σύνδεση Η μετάλλαξη διαχειρίζεται τις συνδέσεις χρηστών—με τον επιτυχή έλεγχο ταυτότητας, θα δημιουργήσει ένα διακριτικό JWT, καθώς και θα επιστρέψει ένα μήνυμα επιτυχίας στην απάντηση.
Τώρα, συμπεριλάβετε το πρόγραμμα επίλυσης ερωτημάτων για την ανάκτηση δεδομένων χρήστη. Για να διασφαλίσετε ότι αυτό το ερώτημα θα είναι προσβάσιμο μόνο σε πιστοποιημένους και εξουσιοδοτημένους χρήστες, συμπεριλάβετε τη λογική εξουσιοδότησης για να περιορίσετε την πρόσβαση μόνο σε χρήστες με διαχειριστής ρόλος.
Ουσιαστικά, το ερώτημα θα ελέγξει πρώτα την εγκυρότητα του διακριτικού και στη συνέχεια, τον ρόλο του χρήστη. Εάν ο έλεγχος εξουσιοδότησης είναι επιτυχής, το ερώτημα επίλυσης θα προχωρήσει στην ανάκτηση και επιστροφή των δεδομένων των χρηστών από τη βάση δεδομένων.
Query: {
users: async (parent, args, context) => {
try {
const token = context.req.headers.authorization || '';
const decodedToken = verifyToken(token);if (decodedToken.role !== 'Admin') {
thrownew ('Unauthorized. Only Admins can access this data.');
}
const users = await User.find({}, { name: 1, _id: 1, role:1 });
return users;
} catch (error) {
console.error(error);
thrownewError('Failed to fetch users');
}
},
},
};
Τέλος, ξεκινήστε τον διακομιστή ανάπτυξης:
node server.js
Φοβερός! Τώρα, προχωρήστε και δοκιμάστε τη λειτουργικότητα του API χρησιμοποιώντας το sandbox του Apollo Server API στο πρόγραμμα περιήγησής σας. Για παράδειγμα, μπορείτε να χρησιμοποιήσετε το κανω ΕΓΓΡΑΦΗ μετάλλαξη για την προσθήκη νέων δεδομένων χρήστη στη βάση δεδομένων και, στη συνέχεια, το Σύνδεση μετάλλαξη για τον έλεγχο ταυτότητας του χρήστη.
Τέλος, προσθέστε το διακριτικό JWT στην ενότητα κεφαλίδας εξουσιοδότησης και προχωρήστε στην αναζήτηση δεδομένων χρήστη στη βάση δεδομένων.
Ασφάλεια GraphQL API
Ο έλεγχος ταυτότητας και η εξουσιοδότηση είναι κρίσιμα στοιχεία για την ασφάλεια των GraphQL API. Ωστόσο, είναι σημαντικό να αναγνωριστεί ότι από μόνες τους μπορεί να μην επαρκούν για τη διασφάλιση συνολικής ασφάλειας. Θα πρέπει να εφαρμόσετε πρόσθετα μέτρα ασφαλείας, όπως επικύρωση εισόδου και κρυπτογράφηση ευαίσθητων δεδομένων.
Υιοθετώντας μια ολοκληρωμένη προσέγγιση ασφαλείας, μπορείτε να προστατεύσετε τα API σας από διαφορετικές πιθανές επιθέσεις.