يعد GraphQL بديلاً شائعًا لبنية RESTful API التقليدية، حيث يقدم استعلام بيانات مرنًا وفعالًا ولغة معالجة لواجهات برمجة التطبيقات. مع ل مع تزايد الاعتماد، أصبح من المهم بشكل متزايد إعطاء الأولوية لأمن واجهات برمجة تطبيقات GraphQL لحماية التطبيقات من الوصول غير المصرح به والبيانات المحتملة الانتهاكات.
أحد الأساليب الفعالة لتأمين واجهات برمجة تطبيقات GraphQL هو تنفيذ JSON Web Tokens (JWTs). توفر JWTs طريقة آمنة وفعالة لمنح الوصول إلى الموارد المحمية وتنفيذ الإجراءات المصرح بها، مما يضمن الاتصال الآمن بين العملاء وواجهات برمجة التطبيقات.
المصادقة والترخيص في واجهات برمجة تطبيقات GraphQL
على عكس واجهات برمجة تطبيقات RESTعادةً ما تحتوي واجهات برمجة تطبيقات GraphQL على نقطة نهاية واحدة تسمح للعملاء بطلب كميات مختلفة من البيانات ديناميكيًا في استعلاماتهم. على الرغم من أن هذه المرونة هي مصدر قوتها، إلا أنها تزيد أيضًا من مخاطر الهجمات الأمنية المحتملة مثل نقاط الضعف في التحكم في الوصول.
للتخفيف من هذه المخاطر، من المهم تنفيذ عمليات مصادقة وتفويض قوية، بما في ذلك تحديد أذونات الوصول بشكل صحيح. ومن خلال القيام بذلك، فإنك تضمن أن المستخدمين المصرح لهم فقط هم من يمكنهم الوصول إلى الموارد المحمية، وفي النهاية، تقليل مخاطر الخروقات الأمنية المحتملة وفقدان البيانات.
يمكنك العثور على رمز هذا المشروع في ملف جيثب مخزن.
قم بإعداد خادم Express.js Apollo
خادم أبولو هو تطبيق خادم GraphQL المستخدم على نطاق واسع لواجهات برمجة تطبيقات GraphQL. يمكنك استخدامه لإنشاء مخططات GraphQL بسهولة، وتحديد وحدات الحل، وإدارة مصادر البيانات المختلفة لواجهات برمجة التطبيقات الخاصة بك.
لإعداد Express.js Apollo Server، قم بإنشاء مجلد مشروع وفتحه:
mkdir graphql-API-jwt
cd graphql-API-jwt
بعد ذلك، قم بتشغيل هذا الأمر لتهيئة مشروع Node.js جديد باستخدامه npm، مدير حزم العقدة:
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 و أدوات الحل المعلمات، مع تحديد المخطط والعمليات التي يمكن لواجهة برمجة التطبيقات التعامل معها. ال سياق يقوم الخيار بتكوين كائن req في سياق كل محلل، مما سيسمح للخادم بالوصول إلى التفاصيل الخاصة بالطلب مثل قيم الرأس.
إنشاء قاعدة بيانات MongoDB
لتأسيس اتصال قاعدة البيانات، أولا إنشاء قاعدة بيانات MongoDB أو قم بإعداد مجموعة على MongoDB Atlas. ثم انسخ سلسلة URI لاتصال قاعدة البيانات المتوفرة، وقم بإنشاء ملف .env الملف، وأدخل سلسلة الاتصال كما يلي:
MONGO_URI=""
تحديد نموذج البيانات
تحديد نموذج بيانات باستخدام Mongoose. إنشاء جديد النماذج/user.js الملف ويتضمن الكود التالي:
const {model, Schema} = require('mongoose');
const userSchema = new Schema({
name: String,
password: String,
role: String
});
module.exports = model('user', userSchema);
تعريف مخطط GraphQL
في واجهة برمجة تطبيقات GraphQL، يحدد المخطط بنية البيانات التي يمكن الاستعلام عنها، بالإضافة إلى تحديد الخطوط العريضة العمليات المتاحة (الاستعلامات والطفرات) التي يمكنك تنفيذها للتفاعل مع البيانات من خلال واجهة برمجة التطبيقات.
لتحديد مخطط، قم بإنشاء مجلد جديد في الدليل الجذر لمشروعك وقم بتسميته 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;
إنشاء حلول لواجهة برمجة تطبيقات GraphQL
تحدد وظائف المحلل كيفية استرداد البيانات استجابة لاستعلامات العميل والطفرات، بالإضافة إلى الحقول الأخرى المحددة في المخطط. عندما يرسل العميل استعلامًا أو طفرة، يقوم خادم GraphQL بتشغيل وحدات الحل المقابلة لمعالجة البيانات المطلوبة وإرجاعها من مصادر مختلفة، مثل قواعد البيانات أو واجهات برمجة التطبيقات.
لتنفيذ المصادقة والترخيص باستخدام JSON Web Tokens (JWTs)، حدد وحدات الحل الخاصة بالسجل وتحولات تسجيل الدخول. سوف يتعامل هؤلاء مع عمليات تسجيل المستخدم والمصادقة. بعد ذلك، قم بإنشاء محلل استعلام جلب البيانات الذي لن يتمكن من الوصول إليه إلا المستخدمين المصادق عليهم والمصرح لهم.
لكن أولاً، حدد الوظائف اللازمة لإنشاء JWTs والتحقق منها. في ال محللون.js الملف، ابدأ بإضافة الواردات التالية.
const User = require("../models/user");
const jwt = require('jsonwebtoken');
const secretKey = process.env.SECRET_KEY;
تأكد من إضافة المفتاح السري الذي ستستخدمه لتوقيع رموز الويب JSON المميزة إلى ملف .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
لتحديد وحدات الحل لواجهة برمجة تطبيقات GraphQL، يتعين عليك تحديد العمليات المحددة التي ستديرها، في هذه الحالة، عمليات تسجيل المستخدم وتسجيل الدخول. أولاً، قم بإنشاء أدوات الحل الكائن الذي سيحتوي على وظائف المحلل، ثم حدد عمليات التحول التالية:
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');
}
}
},
ال يسجل يعالج الطفرة عملية التسجيل عن طريق إضافة بيانات المستخدم الجديدة إلى قاعدة البيانات. بينما ال تسجيل الدخول تدير الطفرة تسجيلات دخول المستخدم - عند المصادقة الناجحة، ستنشئ رمز 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) باستخدام وضع الحماية Apollo Server API في متصفحك. على سبيل المثال، يمكنك استخدام يسجل طفرة لإضافة بيانات مستخدم جديدة في قاعدة البيانات، ومن ثم تسجيل الدخول طفرة لمصادقة المستخدم.
وأخيرًا، أضف رمز JWT المميز إلى قسم رأس التفويض واستمر في الاستعلام عن قاعدة البيانات للحصول على بيانات المستخدم.
تأمين واجهات برمجة تطبيقات GraphQL
تعد المصادقة والترخيص مكونين أساسيين لتأمين واجهات برمجة تطبيقات GraphQL. ومع ذلك، فمن المهم أن ندرك أن هذه التدابير وحدها قد لا تكون كافية لضمان الأمن الشامل. يجب عليك تنفيذ تدابير أمنية إضافية مثل التحقق من صحة الإدخال، وتشفير البيانات الحساسة.
من خلال اعتماد نهج أمني شامل، يمكنك حماية واجهات برمجة التطبيقات الخاصة بك ضد الهجمات المحتملة المختلفة.