GraphQL yra populiari alternatyva tradicinei RESTful API architektūrai, siūlanti lanksčią ir efektyvią API duomenų užklausų ir manipuliavimo kalbą. Su savo vis labiau populiarėjant, tampa vis svarbiau teikti pirmenybę GraphQL API saugai, siekiant apsaugoti programas nuo neteisėtos prieigos ir galimų duomenų. pažeidimai.

Vienas veiksmingas būdas apsaugoti GraphQL API yra JSON žiniatinklio prieigos raktų (JWT) diegimas. JWT yra saugus ir efektyvus būdas suteikti prieigą prie saugomų išteklių ir atlikti įgaliotus veiksmus, užtikrinant saugų ryšį tarp klientų ir API.

Autentifikavimas ir autorizacija GraphQL API

Skirtingai nei REST API, GraphQL API paprastai turi vieną galinį tašką, kuris leidžia klientams dinamiškai prašyti įvairaus duomenų kiekio savo užklausose. Nors šis lankstumas yra jo stiprybė, jis taip pat padidina galimų saugumo atakų, pvz., sugadintų prieigos kontrolės spragų, riziką.

Norint sumažinti šią riziką, svarbu įdiegti patikimus autentifikavimo ir autorizacijos procesus, įskaitant tinkamą prieigos leidimų apibrėžimą. Tai darydami garantuojate, kad tik įgalioti vartotojai gali pasiekti apsaugotus išteklius, ir galiausiai sumažinate galimų saugumo pažeidimų ir duomenų praradimo riziką.

instagram viewer

Šio projekto kodą galite rasti jame GitHub saugykla.

Nustatykite Express.js Apollo serverį

Apollo serveris yra plačiai naudojamas GraphQL serverio diegimas GraphQL API. Galite naudoti jį norėdami lengvai kurti GraphQL schemas, apibrėžti sprendiklius ir valdyti skirtingus API duomenų šaltinius.

Norėdami nustatyti Express.js Apollo serverį, sukurkite ir atidarykite projekto aplanką:

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

Tada paleiskite šią komandą, kad inicijuotumėte naują Node.js projektą naudodami npm, mazgo paketų tvarkyklė:

npm init --yes

Dabar įdiekite šiuos paketus.

npm install apollo-server graphql mongoose jsonwebtokens dotenv

Galiausiai sukurkite a server.js failą šakniniame kataloge ir nustatykite serverį naudodami šį kodą:

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 serveris nustatytas naudojant typeDefs ir sprendėjai parametrus, nurodančius schemą ir operacijas, kurias gali apdoroti API. The kontekste parinktis sukonfigūruoja req objektą pagal kiekvieno sprendiklio kontekstą, o tai leis serveriui pasiekti konkrečią užklausos informaciją, pvz., antraštės reikšmes.

Sukurkite MongoDB duomenų bazę

Norėdami užmegzti duomenų bazės ryšį, pirmiausia sukurti MongoDB duomenų bazę arba sukurti klasterį MongoDB Atlas. Tada nukopijuokite pateiktą duomenų bazės ryšio URI eilutę, sukurkite a .env failą ir įveskite ryšio eilutę taip:

MONGO_URI=""

Apibrėžkite duomenų modelį

Apibrėžkite duomenų modelį naudodami Mongoose. Sukurti naują models/user.js failą ir įtraukite šį kodą:

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

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

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

Apibrėžkite GraphQL schemą

GraphQL API schema apibrėžia duomenų, dėl kurių galima pateikti užklausą, struktūrą, taip pat apibrėžia galimos operacijos (užklausos ir mutacijos), kurias galite atlikti norėdami sąveikauti su duomenimis per API.

Norėdami apibrėžti schemą, savo projekto šakniniame kataloge sukurkite naują aplanką ir pavadinkite jį graphql. Šiame aplanke pridėkite du failus: typeDefs.js ir solvers.js.

Viduje typeDefs.js failą, įtraukite šį 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;

Sukurkite GraphQL API sprendimus

Resolver funkcijos nustato, kaip duomenys gaunami atsakant į kliento užklausas ir mutacijas, taip pat kitus schemoje apibrėžtus laukus. Kai klientas siunčia užklausą arba mutaciją, GraphQL serveris suaktyvina atitinkamus sprendimus, kad apdorotų ir grąžintų reikiamus duomenis iš įvairių šaltinių, tokių kaip duomenų bazės ar API.

Norėdami įdiegti autentifikavimą ir autorizavimą naudodami JSON žiniatinklio prieigos raktus (JWT), apibrėžkite registro ir prisijungimo mutacijų sprendiklius. Jie tvarkys vartotojo registracijos ir autentifikavimo procesus. Tada sukurkite duomenų gavimo užklausų sprendiklį, kuris bus pasiekiamas tik patvirtintiems ir įgaliotiems vartotojams.

Tačiau pirmiausia apibrėžkite JWT generavimo ir tikrinimo funkcijas. Viduje solvers.js failą, pradėkite pridėdami toliau nurodytus importuotus elementus.

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

Prie .env failo būtinai pridėkite slaptąjį raktą, kurį naudosite JSON žiniatinklio prieigos raktams pasirašyti.

SECRET_KEY = '';

Norėdami sugeneruoti autentifikavimo prieigos raktą, įtraukite šią funkciją, kuri taip pat nurodo unikalius JWT prieigos rakto atributus, pvz., galiojimo laiką. Be to, galite įtraukti kitus atributus, pvz., išleistus laiku, atsižvelgiant į konkrečius programos reikalavimus.

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

return token;
}

Dabar įdiekite prieigos rakto patvirtinimo logiką, kad patvirtintumėte JWT prieigos raktus, įtrauktus į vėlesnes HTTP užklausas.

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

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

Ši funkcija paims žetoną kaip įvestį, patikrins jo galiojimą naudodama nurodytą slaptąjį raktą ir grąžins iššifruotą prieigos raktą, jei jis galioja, priešingu atveju pateikia klaidą, nurodant netinkamą prieigos raktą.

Apibrėžkite API sprendinius

Norėdami apibrėžti GraphQL API sprendinius, turite apibūdinti konkrečias operacijas, kurias ji valdys, šiuo atveju vartotojo registracijos ir prisijungimo operacijas. Pirmiausia sukurkite a sprendėjai objektą, kuriame bus rezoliucijos funkcijos, tada apibrėžkite šias mutacijos operacijas:

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 Registruotis mutacija tvarko registracijos procesą įtraukdama į duomenų bazę naujus vartotojo duomenis. Kol Prisijungti mutacija valdo vartotojų prisijungimus – sėkmingo autentifikavimo atveju ji sugeneruos JWT prieigos raktą ir atsakyme pateiks sėkmės pranešimą.

Dabar įtraukite užklausų sprendiklį, skirtą vartotojo duomenims gauti. Norėdami užtikrinti, kad ši užklausa būtų prieinama tik autentifikuotiems ir įgaliotiems naudotojams, įtraukite autorizavimo logiką, kad apribotumėte prieigą tik naudotojams, turintiems Admin vaidmenį.

Iš esmės užklausa pirmiausia patikrins prieigos rakto galiojimą, o tada – vartotojo vaidmenį. Jei autorizavimo patikrinimas bus sėkmingas, sprendiklio užklausa bus tęsiama, kad gautų ir grąžintų vartotojų duomenis iš duomenų bazės.

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

Galiausiai paleiskite kūrimo serverį:

node server.js

Nuostabu! Dabar eikite į priekį ir išbandykite API funkcionalumą naudodami „Apollo Server“ API smėlio dėžę savo naršyklėje. Pavyzdžiui, galite naudoti Registruotis mutaciją, kad į duomenų bazę įtrauktumėte naujų vartotojo duomenų, o tada Prisijungti mutacija vartotojui autentifikuoti.

Galiausiai pridėkite JWT prieigos raktą prie autorizacijos antraštės skyriaus ir pradėkite duomenų bazėje užklausą dėl vartotojo duomenų.

GraphQL API apsauga

Autentifikavimas ir įgaliojimas yra esminiai „GraphQL“ API apsaugos komponentai. Nepaisant to, svarbu pripažinti, kad vien jų gali nepakakti visapusiškam saugumui užtikrinti. Turėtumėte įdiegti papildomas saugos priemones, pvz., įvesties patvirtinimą ir jautrių duomenų šifravimą.

Taikydami visapusišką saugos metodą galite apsaugoti savo API nuo įvairių galimų atakų.