GraphQL е популярна алтернатива на традиционната RESTful API архитектура, предлагаща гъвкав и ефективен език за заявки и манипулиране на данни за API. С неговите нарастващо приемане, става все по-важно да се даде приоритет на сигурността на API на GraphQL, за да се защитят приложенията от неоторизиран достъп и потенциални данни нарушения.

Един ефективен подход за защита на GraphQL API е внедряването на JSON уеб токени (JWT). JWT предоставят сигурен и ефективен метод за предоставяне на достъп до защитени ресурси и извършване на оторизирани действия, осигурявайки сигурна комуникация между клиенти и API.

Удостоверяване и оторизация в API на GraphQL

За разлика от API на REST, GraphQL API обикновено имат една крайна точка, която позволява на клиентите динамично да изискват различни количества данни в своите заявки. Въпреки че тази гъвкавост е неговата сила, тя също така увеличава риска от потенциални атаки срещу сигурността, като например повредени уязвимости в контрола на достъпа.

За да намалите този риск, е важно да внедрите стабилни процеси за удостоверяване и оторизация, включително правилно дефиниране на разрешения за достъп. По този начин вие гарантирате, че само оторизирани потребители имат достъп до защитени ресурси и в крайна сметка намалявате риска от потенциални пробиви в сигурността и загуба на данни.

instagram viewer

Можете да намерите кода на този проект в него 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

И накрая, създайте a 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 може да обработва. The контекст опцията конфигурира обекта req към контекста на всеки резолвер, което ще позволи на сървъра да има достъп до специфични за заявката подробности, като стойности на заглавка.

Създайте MongoDB база данни

За да установите връзка с базата данни, първо създайте база данни MongoDB или настройте клъстер на MongoDB Atlas. След това копирайте предоставения URI низ за връзка с базата данни, създайте a .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

В API на GraphQL схемата дефинира структурата на данните, които могат да бъдат заявени, както и очертава наличните операции (заявки и мутации), които можете да изпълнявате, за да взаимодействате с данни чрез API.

За да дефинирате схема, създайте нова папка в главната директория на вашия проект и я наименувайте graphql. В тази папка добавете два файла: typeDefs.js и resolvers.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 API

Функциите за преобразуване определят как данните се извличат в отговор на клиентски заявки и мутации, както и други полета, дефинирани в схемата. Когато клиент изпрати заявка или мутация, GraphQL сървърът задейства съответните резолвери за обработка и връщане на необходимите данни от различни източници, като бази данни или API.

За да приложите удостоверяване и оторизация с помощта на JSON уеб токени (JWT), дефинирайте резолвери за мутациите в регистъра и влизането. Те ще управляват процесите на регистрация на потребители и удостоверяване. След това създайте преобразувател на заявки за извличане на данни, който ще бъде достъпен само за удостоверени и оторизирани потребители.

Но първо дефинирайте функциите за генериране и проверка на JWT. В resolvers.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

За да дефинирате резолверите за API на GraphQL, трябва да очертаете конкретните операции, които ще управлява, в този случай операциите за регистрация на потребител и влизане. Първо създайте a резолвери обект, който ще съдържа функциите на резолвера, след това дефинирайте следните операции за мутация:

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');
}
}
},

The регистрирам 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 с помощта на пясъчника на Apollo Server API във вашия браузър. Например, можете да използвате регистрирам мутация за добавяне на нови потребителски данни в базата данни и след това, the Влизам мутация за удостоверяване на потребителя.

И накрая, добавете JWT токена към секцията за заглавка на оторизацията и продължете да правите заявки в базата данни за потребителски данни.

Защита на GraphQL API

Удостоверяването и оторизацията са ключови компоненти за защита на GraphQL API. Независимо от това, важно е да се признае, че те сами по себе си може да не са достатъчни, за да осигурят пълна сигурност. Трябва да приложите допълнителни мерки за сигурност като валидиране на входа и криптиране на чувствителни данни.

Като възприемете цялостен подход за сигурност, можете да защитите своите API срещу различни потенциални атаки.