GraphQL er et populært alternativ til tradisjonell RESTful API-arkitektur, og tilbyr et fleksibelt og effektivt dataspørrings- og manipulasjonsspråk for APIer. Med dens økende bruk, blir det stadig viktigere å prioritere sikkerheten til GraphQL APIer for å beskytte applikasjoner mot uautorisert tilgang og potensielle data brudd.

En effektiv tilnærming for å sikre GraphQL APIer er å implementere JSON Web Tokens (JWTs). JWT-er gir en sikker og effektiv metode for å gi tilgang til beskyttede ressurser og utføre autoriserte handlinger, og sikrer sikker kommunikasjon mellom klienter og API-er.

Autentisering og autorisasjon i GraphQL APIer

I motsetning til REST APIer, GraphQL APIer har vanligvis et enkelt endepunkt som lar klienter dynamisk be om ulike mengder data i spørringene sine. Selv om denne fleksibiliteten er dens styrke, øker den også risikoen for potensielle sikkerhetsangrep som for eksempel ødelagte sikkerhetsproblemer.

For å redusere denne risikoen er det viktig å implementere robuste autentiserings- og autorisasjonsprosesser, inkludert riktig definering av tilgangstillatelser. Ved å gjøre det garanterer du at bare autoriserte brukere kan få tilgang til beskyttede ressurser, og til slutt reduserer du risikoen for potensielle sikkerhetsbrudd og tap av data.

instagram viewer

Du kan finne dette prosjektets kode i sin GitHub oppbevaringssted.

Sett opp en Express.js Apollo-server

Apollo server er en mye brukt GraphQL-serverimplementering for GraphQL APIer. Du kan bruke den til å enkelt bygge GraphQL-skjemaer, definere resolvere og administrere forskjellige datakilder for API-ene dine.

For å sette opp en Express.js Apollo Server, opprette og åpne en prosjektmappe:

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

Deretter kjører du denne kommandoen for å initialisere et nytt Node.js-prosjekt ved hjelp av npm, Node-pakkebehandleren:

npm init --yes

Installer nå disse pakkene.

npm install apollo-server graphql mongoose jsonwebtokens dotenv

Til slutt oppretter du en server.js fil i rotkatalogen, og sett opp serveren din med denne koden:

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-serveren er satt opp med typeDefs og løsere parametere, som spesifiserer skjemaet og operasjonene som API-en kan håndtere. De kontekst alternativet konfigurerer req-objektet til konteksten til hver resolver, noe som vil tillate serveren å få tilgang til forespørselsspesifikke detaljer som overskriftsverdier.

Opprett en MongoDB-database

For å etablere databaseforbindelsen, først opprette en MongoDB-database eller sette opp en klynge på MongoDB Atlas. Deretter kopierer du den angitte URI-strengen for databasetilkoblingen, oppretter en .env fil, og skriv inn tilkoblingsstrengen som følger:

MONGO_URI=""

Definer datamodellen

Definer en datamodell ved å bruke Mongoose. Lage en ny modeller/bruker.js fil og inkludere følgende kode:

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

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

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

Definer GraphQL-skjemaet

I en GraphQL API definerer skjemaet strukturen til dataene som kan spørres, samt skisserer de tilgjengelige operasjonene (spørringer og mutasjoner) som du kan utføre for å samhandle med data gjennom API.

For å definere et skjema, opprette en ny mappe i rotkatalogen til prosjektet og gi den et navn graphql. Legg til to filer i denne mappen: typeDefs.js og resolvers.js.

I typeDefs.js fil, inkluderer følgende kode:

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;

Opprett løsere for GraphQL API

Resolverfunksjoner bestemmer hvordan data hentes som svar på klientforespørsler og mutasjoner, samt andre felt definert i skjemaet. Når en klient sender en spørring eller mutasjon, utløser GraphQL-serveren de tilsvarende resolverne til å behandle og returnere de nødvendige dataene fra ulike kilder, for eksempel databaser eller APIer.

For å implementere autentisering og autorisasjon ved hjelp av JSON Web Tokens (JWTs), definer resolvere for register- og påloggingsmutasjonene. Disse vil håndtere prosessene med brukerregistrering og autentisering. Deretter oppretter du en søkeløser for datahenting som bare vil være tilgjengelig for autentiserte og autoriserte brukere.

Men først, definer funksjonene for å generere og verifisere JWT-ene. I resolvers.js fil, start med å legge til følgende importer.

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

Sørg for å legge til den hemmelige nøkkelen du vil bruke til å signere JSON-netttokens til .env-filen.

SECRET_KEY = '';

For å generere et autentiseringstoken, inkluderer følgende funksjon, som også spesifiserer unike attributter for JWT-tokenet, f.eks. utløpstiden. I tillegg kan du inkludere andre attributter som utstedt på et tidspunkt basert på dine spesifikke applikasjonskrav.

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

return token;
}

Implementer nå tokenverifiseringslogikken for å validere JWT-tokenene som er inkludert i påfølgende HTTP-forespørsler.

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

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

Denne funksjonen vil ta et token som input, verifisere dets gyldighet ved hjelp av den spesifiserte hemmelige nøkkelen, og returnere det dekodede tokenet hvis det er gyldig, ellers gir en feilmelding som indikerer et ugyldig token.

Definer API-løsere

For å definere resolvere for GraphQL API, må du skissere de spesifikke operasjonene den skal administrere, i dette tilfellet brukerregistrering og påloggingsoperasjoner. Lag først en løsere objekt som vil inneholde resolver-funksjonene, definerer deretter følgende mutasjonsoperasjoner:

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 registrere mutasjon håndterer registreringsprosessen ved å legge til nye brukerdata til databasen. Mens Logg Inn mutasjon administrerer brukerpålogginger - ved vellykket autentisering vil den generere et JWT-token, samt returnere en suksessmelding i svaret.

Ta med spørringsløseren for å hente brukerdata. For å sikre at denne spørringen bare vil være tilgjengelig for autentiserte og autoriserte brukere, inkluderer du autorisasjonslogikk for å begrense tilgangen til kun brukere med en Admin rolle.

I hovedsak vil spørringen først sjekke gyldigheten til tokenet og deretter brukerrollen. Hvis autorisasjonssjekken er vellykket, fortsetter resolver-spørringen for å hente og returnere brukernes data fra databasen.

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

Til slutt, start utviklingsserveren:

node server.js

Rått! Nå, fortsett og test funksjonaliteten til API ved å bruke Apollo Server API-sandkassen i nettleseren din. Du kan for eksempel bruke registrere mutasjon for å legge til nye brukerdata i databasen, og deretter Logg Inn mutasjon for å autentisere brukeren.

Til slutt legger du til JWT-tokenet i autorisasjonshodedelen og fortsett med å spørre databasen etter brukerdata.

Sikring av GraphQL APIer

Autentisering og autorisasjon er avgjørende komponenter for å sikre GraphQL APIer. Ikke desto mindre er det viktig å erkjenne at de alene kanskje ikke er tilstrekkelige for å sikre omfattende sikkerhet. Du bør implementere ytterligere sikkerhetstiltak som inndatavalidering og kryptering av sensitive data.

Ved å ta i bruk en omfattende sikkerhetstilnærming kan du beskytte API-ene dine mot ulike potensielle angrep.