GraphQL is een populair alternatief voor de traditionele RESTful API-architectuur en biedt een flexibele en efficiënte dataquery- en manipulatietaal voor API's. Met zijn Door de toenemende acceptatie wordt het steeds belangrijker om prioriteit te geven aan de beveiliging van GraphQL API's om applicaties te beschermen tegen ongeoorloofde toegang en potentiële gegevens inbreuken.

Een effectieve aanpak voor het beveiligen van GraphQL API's is het implementeren van JSON Web Tokens (JWT's). JWT's bieden een veilige en efficiënte methode voor het verlenen van toegang tot beschermde bronnen en het uitvoeren van geautoriseerde acties, waardoor veilige communicatie tussen clients en API's wordt gegarandeerd.

Authenticatie en autorisatie in GraphQL API's

in tegenstelling tot REST API's, hebben GraphQL API's doorgaans één enkel eindpunt waarmee klanten dynamisch verschillende hoeveelheden gegevens in hun zoekopdrachten kunnen opvragen. Hoewel deze flexibiliteit zijn kracht is, vergroot het ook het risico op potentiële beveiligingsaanvallen, zoals gebroken kwetsbaarheden in de toegangscontrole.

instagram viewer

Om dit risico te beperken, is het belangrijk om robuuste authenticatie- en autorisatieprocessen te implementeren, inclusief het correct definiëren van toegangsrechten. Door dit te doen, garandeert u dat alleen geautoriseerde gebruikers toegang hebben tot beschermde bronnen en verkleint u uiteindelijk het risico op mogelijke beveiligingsinbreuken en gegevensverlies.

U kunt de code van dit project vinden in zijn GitHub opslagplaats.

Stel een Express.js Apollo-server in

Apollo-server is een veelgebruikte GraphQL-serverimplementatie voor GraphQL API's. U kunt het gebruiken om eenvoudig GraphQL-schema's te bouwen, oplossers te definiëren en verschillende gegevensbronnen voor uw API's te beheren.

Om een ​​Express.js Apollo Server in te stellen, maakt en opent u een projectmap:

mkdir graphql-API-jwt
cd graphql-API-jwt

Voer vervolgens deze opdracht uit om een ​​nieuw Node.js-project te initialiseren met behulp van npm, de knooppuntpakketbeheerder:

npm init --yes

Installeer nu deze pakketten.

npm install apollo-server graphql mongoose jsonwebtokens dotenv

Maak ten slotte een server.js bestand in de hoofdmap en stel uw server in met deze code:

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

De GraphQL-server is ingesteld met de typeDefs En oplossers parameters, die het schema en de bewerkingen specificeren die de API aankan. De context optie configureert het req-object voor de context van elke solver, waardoor de server toegang krijgt tot verzoekspecifieke details zoals headerwaarden.

Maak een MongoDB-database

Om eerst de databaseverbinding tot stand te brengen maak een MongoDB-database of een cluster opgezet op MongoDB Atlas. Kopieer vervolgens de opgegeven URI-tekenreeks voor de databaseverbinding, maak een .env bestand en voer de verbindingsreeks als volgt in:

MONGO_URI=""

Definieer het gegevensmodel

Definieer een gegevensmodel met Mongoose. Maak een nieuwe modellen/gebruiker.js bestand en voeg de volgende code toe:

const {model, Schema} = require('mongoose');

const userSchema = new Schema({
name: String,
password: String,
role: String
});

module.exports = model('user', userSchema);

Definieer het GraphQL-schema

In een GraphQL API definieert het schema de structuur van de gegevens die kunnen worden opgevraagd, en schetst het ook de beschikbare bewerkingen (query's en mutaties) die u kunt uitvoeren om met gegevens te communiceren via de API.

Om een ​​schema te definiëren, maakt u een nieuwe map in de hoofdmap van uw project en geeft u deze een naam grafiekql. Voeg in deze map twee bestanden toe: typeDefs.js En oplossers.js.

In de typeDefs.js bestand, bevat de volgende code:

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;

Maak Resolvers voor de GraphQL API

Resolverfuncties bepalen hoe gegevens worden opgehaald als reactie op vragen en mutaties van klanten, evenals op andere velden die in het schema zijn gedefinieerd. Wanneer een client een query of mutatie verzendt, activeert de GraphQL-server de bijbehorende solvers om de benodigde gegevens uit verschillende bronnen, zoals databases of API's, te verwerken en terug te sturen.

Om authenticatie en autorisatie te implementeren met behulp van JSON Web Tokens (JWT's), definieert u solvers voor de register- en login-mutaties. Deze zullen de processen van gebruikersregistratie en authenticatie afhandelen. Maak vervolgens een query-oplosser voor het ophalen van gegevens die alleen toegankelijk is voor geverifieerde en geautoriseerde gebruikers.

Maar definieer eerst de functies om de JWT's te genereren en te verifiëren. In de oplossers.js bestand, begin met het toevoegen van de volgende import.

const User = require("../models/user");
const jwt = require('jsonwebtoken');
const secretKey = process.env.SECRET_KEY;

Zorg ervoor dat u de geheime sleutel die u gaat gebruiken om JSON-webtokens te ondertekenen, toevoegt aan het .env-bestand.

SECRET_KEY = '';

Om een ​​authenticatietoken te genereren, neemt u de volgende functie op, die ook unieke kenmerken voor het JWT-token specificeert, bijvoorbeeld de vervaltijd. Bovendien kunt u andere kenmerken opnemen, zoals op tijd uitgegeven, op basis van uw specifieke toepassingsvereisten.

functiongenerateToken(user) {
const token = jwt.sign(
{ id: user.id, role: user.role },
secretKey,
{ expiresIn: '1h', algorithm: 'HS256' }
 );

return token;
}

Implementeer nu de tokenverificatielogica om de JWT-tokens te valideren die zijn opgenomen in volgende HTTP-aanvragen.

functionverifyToken(token) {
if (!token) {
thrownewError('Token not provided');
}

try {
const decoded = jwt.verify(token, secretKey, { algorithms: ['HS256'] });
return decoded;
} catch (err) {
thrownewError('Invalid token');
}
}

Deze functie neemt een token als invoer, verifieert de geldigheid ervan met behulp van de opgegeven geheime sleutel en retourneert het gedecodeerde token als het geldig is, anders genereert het een fout die een ongeldig token aangeeft.

Definieer de API-resolvers

Om de solvers voor de GraphQL API te definiëren, moet u de specifieke bewerkingen schetsen die deze zal beheren, in dit geval de gebruikersregistratie en aanmeldingsbewerkingen. Maak eerst een oplossers object dat de resolverfuncties zal bevatten, definieer vervolgens de volgende mutatiebewerkingen:

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

De register Mutatie verzorgt het registratieproces door de nieuwe gebruikersgegevens aan de database toe te voegen. Terwijl de Log in Mutatie beheert gebruikersaanmeldingen: bij succesvolle authenticatie genereert het een JWT-token en retourneert het een succesbericht in het antwoord.

Voeg nu de query-oplosser toe voor het ophalen van gebruikersgegevens. Om ervoor te zorgen dat deze zoekopdracht alleen toegankelijk is voor geverifieerde en geautoriseerde gebruikers, moet u autorisatielogica opnemen om de toegang te beperken tot alleen gebruikers met een beheerder rol.

In wezen controleert de query eerst de geldigheid van het token en vervolgens de gebruikersrol. Als de autorisatiecontrole succesvol is, gaat de oplosserquery verder met het ophalen en retourneren van de gebruikersgegevens uit de database.

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

Start ten slotte de ontwikkelingsserver:

node server.js

Geweldig! Ga nu door en test de functionaliteit van de API met behulp van de Apollo Server API-sandbox in uw browser. U kunt bijvoorbeeld gebruik maken van de register mutatie om nieuwe gebruikersgegevens aan de database toe te voegen, en vervolgens de Log in mutatie om de gebruiker te authenticeren.

Voeg ten slotte het JWT-token toe aan de autorisatiekopsectie en ga verder met het doorzoeken van de database op gebruikersgegevens.

Het beveiligen van GraphQL API's

Authenticatie en autorisatie zijn cruciale componenten voor het beveiligen van GraphQL API's. Niettemin is het belangrijk om te onderkennen dat deze op zichzelf wellicht niet voldoende zijn om alomvattende veiligheid te garanderen. U moet aanvullende beveiligingsmaatregelen implementeren, zoals invoervalidatie en versleuteling van gevoelige gegevens.

Door een alomvattende beveiligingsaanpak te hanteren, kunt u uw API's beschermen tegen verschillende potentiële aanvallen.