GraphQL er et populært alternativ til traditionel RESTful API-arkitektur, der tilbyder et fleksibelt og effektivt dataforespørgsel og manipulationssprog til API'er. Med sin voksende anvendelse bliver det stadig vigtigere at prioritere sikkerheden af ​​GraphQL API'er for at beskytte applikationer mod uautoriseret adgang og potentielle data brud.

En effektiv tilgang til sikring af GraphQL API'er er implementering af JSON Web Tokens (JWT'er). JWT'er giver en sikker og effektiv metode til at give adgang til beskyttede ressourcer og udføre autoriserede handlinger, hvilket sikrer sikker kommunikation mellem klienter og API'er.

Godkendelse og autorisation i GraphQL API'er

I modsætning til REST API'er, GraphQL API'er har typisk et enkelt slutpunkt, der giver klienter mulighed for dynamisk at anmode om varierende mængder af data i deres forespørgsler. Selvom denne fleksibilitet er dens styrke, øger den også risikoen for potentielle sikkerhedsangreb, såsom brudte adgangskontrolsårbarheder.

For at mindske denne risiko er det vigtigt at implementere robuste godkendelses- og godkendelsesprocesser, herunder korrekt definering af adgangstilladelser. Ved at gøre det garanterer du, at kun autoriserede brugere kan få adgang til beskyttede ressourcer, og i sidste ende reducerer du risikoen for potentielle sikkerhedsbrud og tab af data.

instagram viewer

Du kan finde dette projekts kode i dens GitHub depot.

Konfigurer en Express.js Apollo-server

Apollo server er en meget brugt GraphQL-serverimplementering til GraphQL API'er. Du kan bruge det til nemt at bygge GraphQL-skemaer, definere resolvere og administrere forskellige datakilder til dine API'er.

For at konfigurere en Express.js Apollo Server skal du oprette og åbne en projektmappe:

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

Kør derefter denne kommando for at initialisere et nyt Node.js-projekt ved hjælp af npm, Node-pakkeadministratoren:

npm init --yes

Installer nu disse pakker.

npm install apollo-server graphql mongoose jsonwebtokens dotenv

Til sidst skal du oprette en server.js fil i rodmappen, og sæt din server op med denne kode:

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 sat op med typeDefs og resolvere parametre, der angiver skemaet og operationerne, som API'en kan håndtere. Det sammenhæng option konfigurerer req-objektet til konteksten af ​​hver resolver, hvilket giver serveren adgang til anmodningsspecifikke detaljer såsom header-værdier.

Opret en MongoDB-database

For at etablere databaseforbindelsen, først oprette en MongoDB-database eller oprette en klynge på MongoDB Atlas. Kopier derefter den medfølgende URI-streng for databaseforbindelse, opret en .env fil, og indtast forbindelsesstrengen som følger:

MONGO_URI=""

Definer datamodellen

Definer en datamodel ved hjælp af Mongoose. Opret en ny models/user.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-skemaet

I en GraphQL API definerer skemaet strukturen af ​​de data, der kan forespørges på, samt skitserer de tilgængelige operationer (forespørgsler og mutationer), som du kan udføre for at interagere med data gennem API.

For at definere et skema skal du oprette en ny mappe i dit projekts rodbibliotek og navngive den graphql. Tilføj to filer i denne mappe: typeDefs.js og resolvers.js.

I typeDefs.js fil, skal du inkludere 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;

Opret resolvere til GraphQL API

Resolver-funktioner bestemmer, hvordan data hentes som svar på klientforespørgsler og mutationer, såvel som andre felter, der er defineret i skemaet. Når en klient sender en forespørgsel eller mutation, udløser GraphQL-serveren de tilsvarende resolvere til at behandle og returnere de nødvendige data fra forskellige kilder, såsom databaser eller API'er.

For at implementere godkendelse og godkendelse ved hjælp af JSON Web Tokens (JWT'er), skal du definere resolvere for register- og loginmutationer. Disse vil håndtere processerne for brugerregistrering og autentificering. Opret derefter en datahentningsforespørgselsløser, som kun vil være tilgængelig for godkendte og autoriserede brugere.

Men først skal du definere funktionerne til at generere og verificere JWT'erne. I resolvers.js fil, start med at tilføje følgende importer.

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

Sørg for at tilføje den hemmelige nøgle, du vil bruge til at signere JSON-webtokens til .env-filen.

SECRET_KEY = '';

For at generere et godkendelsestoken skal du inkludere følgende funktion, som også specificerer unikke attributter for JWT-tokenet, f.eks. udløbstiden. Derudover kan du inkorporere andre attributter, f.eks. udstedt på et tidspunkt baseret på dine specifikke applikationskrav.

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

return token;
}

Implementer nu token-verifikationslogikken for at validere JWT-tokens, der er inkluderet i efterfølgende HTTP-anmodninger.

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 funktion tager et token som input, verificerer dets gyldighed ved hjælp af den angivne hemmelige nøgle og returnerer det afkodede token, hvis det er gyldigt, ellers afgiver en fejl, der indikerer et ugyldigt token.

Definer API-resolvere

For at definere resolverne til GraphQL API'en skal du skitsere de specifikke operationer, den vil administrere, i dette tilfælde brugerregistrering og login-operationer. Først skal du oprette en resolvere objekt, der vil indeholde resolverfunktionerne, definerer derefter følgende mutationsoperationer:

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

Det Tilmeld mutation håndterer registreringsprocessen ved at tilføje de nye brugerdata til databasen. Mens Log på mutation administrerer brugerlogins - ved vellykket godkendelse genererer den et JWT-token samt returnerer en succesmeddelelse i svaret.

Inkluder nu forespørgselsresolveren til at hente brugerdata. For at sikre, at denne forespørgsel kun vil være tilgængelig for godkendte og autoriserede brugere, skal du inkludere autorisationslogik for at begrænse adgangen til kun brugere med en Admin rolle.

Grundlæggende vil forespørgslen først kontrollere gyldigheden af ​​tokenet og derefter brugerrollen. Hvis godkendelseskontrollen lykkes, fortsætter resolver-forespørgslen med at hente og returnere brugernes 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');
}
},
},
};

Start endelig udviklingsserveren:

node server.js

Fantastisk! Gå nu videre og test funktionaliteten af ​​API'et ved hjælp af Apollo Server API-sandkassen i din browser. For eksempel kan du bruge Tilmeld mutation for at tilføje nye brugerdata i databasen, og derefter Log på mutation for at autentificere brugeren.

Til sidst skal du tilføje JWT-tokenet til autorisationshovedsektionen og fortsætte med at forespørge databasen for brugerdata.

Sikring af GraphQL API'er

Autentificering og autorisation er afgørende komponenter for at sikre GraphQL API'er. Ikke desto mindre er det vigtigt at erkende, at de alene måske ikke er tilstrækkelige til at sikre omfattende sikkerhed. Du bør implementere yderligere sikkerhedsforanstaltninger som inputvalidering og kryptering af følsomme data.

Ved at anvende en omfattende sikkerhedstilgang kan du beskytte dine API'er mod forskellige potentielle angreb.