GraphQL je popularna alternativa tradicionalnoj RESTful API arhitekturi, koja nudi fleksibilan i učinkovit jezik za upite podataka i manipulaciju za API-je. Sa svojim sve većim prihvaćanjem, postaje sve važnije dati prioritet sigurnosti GraphQL API-ja kako bi zaštitili aplikacije od neovlaštenog pristupa i potencijalnih podataka kršenja.

Jedan učinkovit pristup za osiguranje GraphQL API-ja je implementacija JSON web tokena (JWT). JWT-ovi pružaju sigurnu i učinkovitu metodu za odobravanje pristupa zaštićenim resursima i izvođenje ovlaštenih radnji, osiguravajući sigurnu komunikaciju između klijenata i API-ja.

Autentifikacija i autorizacija u GraphQL API-jima

Za razliku od REST API-ji, GraphQL API-ji obično imaju jednu krajnju točku koja omogućuje klijentima da dinamički zatraže različite količine podataka u svojim upitima. Iako je ova fleksibilnost njegova snaga, ona također povećava rizik od potencijalnih sigurnosnih napada kao što su slabe točke neispravne kontrole pristupa.

Kako biste ublažili ovaj rizik, važno je implementirati robusne procese provjere autentičnosti i autorizacije, uključujući ispravno definiranje dopuštenja pristupa. Na taj način jamčite da samo ovlašteni korisnici mogu pristupiti zaštićenim resursima i u konačnici smanjujete rizik od mogućih proboja sigurnosti i gubitka podataka.

instagram viewer

Kôd ovog projekta možete pronaći u GitHub spremište.

Postavite Express.js Apollo poslužitelj

Apollo poslužitelj široko je korištena implementacija GraphQL poslužitelja za GraphQL API-je. Možete ga koristiti za jednostavnu izradu GraphQL shema, definiranje rezolvera i upravljanje različitim izvorima podataka za vaše API-je.

Za postavljanje poslužitelja Express.js Apollo, stvorite i otvorite mapu projekta:

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

Zatim pokrenite ovu naredbu da inicijalizirate novi Node.js projekt koristeći npm, upravitelj paketa Node:

npm init --yes

Sada instalirajte ove pakete.

npm install apollo-server graphql mongoose jsonwebtokens dotenv

Na kraju, stvorite a server.js datoteku u korijenskom direktoriju i postavite svoj poslužitelj ovim kodom:

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 poslužitelj je postavljen s typeDefs i razrješivači parametri, određujući shemu i operacije koje API može obraditi. The kontekst opcija konfigurira req objekt prema kontekstu svakog razrješitelja, što će omogućiti poslužitelju pristup detaljima specifičnim za zahtjev kao što su vrijednosti zaglavlja.

Stvorite MongoDB bazu podataka

Da biste uspostavili vezu s bazom podataka, prvo stvoriti MongoDB bazu podataka ili postaviti klaster na MongoDB Atlas. Zatim kopirajte navedeni URI niz veze s bazom podataka, stvorite a .env datoteku i unesite niz veze na sljedeći način:

MONGO_URI=""

Definirajte podatkovni model

Definirajte model podataka koristeći Mongoose. Stvorite novi modeli/korisnik.js datoteku i uključite sljedeći kod:

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

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

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

Definirajte GraphQL shemu

U GraphQL API-ju, shema definira strukturu podataka koji se mogu pretraživati, kao i ocrtavanje dostupne operacije (upiti i mutacije) koje možete izvesti za interakciju s podacima putem API.

Da biste definirali shemu, stvorite novu mapu u korijenskom direktoriju svog projekta i dajte joj naziv graphql. Unutar ove mape dodajte dvije datoteke: typeDefs.js i razrješivači.js.

u typeDefs.js datoteku, uključite sljedeći kod:

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;

Stvorite razlučivače za GraphQL API

Funkcije razrješitelja određuju kako se podaci dohvaćaju kao odgovor na klijentske upite i mutacije, kao i druga polja definirana u shemi. Kada klijent pošalje upit ili mutaciju, GraphQL poslužitelj pokreće odgovarajuće rezolvere za obradu i vraćanje potrebnih podataka iz različitih izvora, kao što su baze podataka ili API-ji.

Za implementaciju provjere autentičnosti i autorizacije korištenjem JSON web tokena (JWT), definirajte razlučivače za mutacije registra i prijave. Oni će upravljati procesima registracije korisnika i provjere autentičnosti. Zatim izradite razrješivač upita za dohvaćanje podataka koji će biti dostupan samo autentificiranim i ovlaštenim korisnicima.

Ali prvo definirajte funkcije za generiranje i provjeru JWT-ova. u razrješivači.js datoteku, započnite dodavanjem sljedećih uvoza.

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

Obavezno dodajte tajni ključ koji ćete koristiti za potpisivanje JSON web tokena u .env datoteku.

SECRET_KEY = '';

Da biste generirali autentifikacijski token, uključite sljedeću funkciju, koja također navodi jedinstvene atribute za JWT token, npr. vrijeme isteka. Dodatno, možete uključiti druge atribute kao što je izdano u određeno vrijeme na temelju vaših specifičnih zahtjeva aplikacije.

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

return token;
}

Sada implementirajte logiku provjere tokena za provjeru valjanosti JWT tokena uključenih u naredne HTTP zahtjeve.

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

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

Ova funkcija će uzeti token kao ulaz, provjeriti njegovu valjanost pomoću navedenog tajnog ključa i vratiti dekodirani token ako je važeći, u suprotnom izbacuje pogrešku koja ukazuje na nevažeći token.

Definirajte API razlučivače

Da biste definirali razrješivače za GraphQL API, trebate navesti specifične operacije kojima će upravljati, u ovom slučaju, operacije registracije korisnika i prijave. Najprije stvorite a razrješivači objekt koji će sadržavati funkcije razlučivača, zatim definirajte sljedeće operacije mutacije:

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 Registar mutation upravlja procesom registracije dodavanjem novih korisničkih podataka u bazu podataka. Dok prijaviti se mutacija upravlja prijavama korisnika—nakon uspješne provjere autentičnosti, generirat će JWT token, kao i vratiti poruku o uspješnosti u odgovoru.

Sada uključite rješavač upita za dohvaćanje korisničkih podataka. Kako biste osigurali da će ovaj upit biti dostupan samo autentificiranim i ovlaštenim korisnicima, uključite logiku autorizacije da ograničite pristup samo korisnicima s Administrator uloga.

U suštini, upit će prvo provjeriti valjanost tokena, a zatim korisničku ulogu. Ako je provjera autorizacije uspješna, upit za rješavanje će nastaviti s dohvaćanjem i vraćanjem korisničkih podataka iz baze podataka.

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

Na kraju pokrenite razvojni poslužitelj:

node server.js

Super! Sada samo naprijed i testirajte funkcionalnost API-ja pomoću Apollo Server API sandboxa u vašem pregledniku. Na primjer, možete koristiti Registar mutacija za dodavanje novih korisničkih podataka u bazu podataka, a zatim, prijaviti se mutacija za autentifikaciju korisnika.

Na kraju, dodajte JWT token u odjeljak zaglavlja autorizacije i nastavite postavljati upit bazi podataka za korisničke podatke.

Zaštita GraphQL API-ja

Autentifikacija i autorizacija ključne su komponente za osiguranje GraphQL API-ja. Unatoč tome, važno je prepoznati da oni sami po sebi možda neće biti dovoljni za osiguranje sveobuhvatne sigurnosti. Trebali biste primijeniti dodatne sigurnosne mjere poput provjere valjanosti unosa i šifriranja osjetljivih podataka.

Usvajanjem sveobuhvatnog sigurnosnog pristupa, možete zaštititi svoje API-je od različitih potencijalnih napada.