GraphQL ir populāra alternatīva tradicionālajai RESTful API arhitektūrai, kas piedāvā elastīgu un efektīvu datu vaicājumu un manipulācijas valodu API. Ar to pieaugot, kļūst arvien svarīgāk par prioritāti noteikt GraphQL API drošību, lai aizsargātu lietojumprogrammas no nesankcionētas piekļuves un iespējamiem datiem. pārkāpumiem.

Viena efektīva pieeja GraphQL API nodrošināšanai ir JSON tīmekļa marķieru (JWT) ieviešana. JWT nodrošina drošu un efektīvu metodi piekļuves piešķiršanai aizsargātiem resursiem un autorizētu darbību veikšanai, nodrošinot drošu komunikāciju starp klientiem un API.

Autentifikācija un autorizācija GraphQL API

Atšķirībā no REST API, GraphQL API parasti ir viens galapunkts, kas ļauj klientiem savos vaicājumos dinamiski pieprasīt dažādu datu apjomu. Lai gan šī elastība ir tās stiprā puse, tā arī palielina iespējamo drošības uzbrukumu risku, piemēram, bojātas piekļuves kontroles ievainojamības.

Lai mazinātu šo risku, ir svarīgi ieviest stabilus autentifikācijas un autorizācijas procesus, tostarp pareizi definēt piekļuves atļaujas. To darot, jūs garantējat, ka tikai autorizēti lietotāji var piekļūt aizsargātajiem resursiem, un galu galā samazināsiet iespējamo drošības pārkāpumu un datu zuduma risku.

Jūs varat atrast šī projekta kodu tajā GitHub krātuve.

Iestatiet Express.js Apollo serveri

Apollo serveris ir plaši izmantota GraphQL servera ieviešana GraphQL API. Varat to izmantot, lai viegli izveidotu GraphQL shēmas, definētu atrisinātājus un pārvaldītu dažādus datu avotus savām API.

Lai iestatītu Express.js Apollo serveri, izveidojiet un atveriet projekta mapi:

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

Pēc tam palaidiet šo komandu, lai inicializētu jaunu Node.js projektu, izmantojot npm, mezgla pakotņu pārvaldnieks:

npm init --yes

Tagad instalējiet šīs pakotnes.

npm install apollo-server graphql mongoose jsonwebtokens dotenv

Visbeidzot, izveidojiet a serveris.js failu saknes direktorijā un iestatiet savu serveri ar šo kodu:

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 ir iestatīts ar typeDefs un atrisinātāji parametrus, norādot shēmu un darbības, ko API var apstrādāt. The kontekstā opcija konfigurē req objektu katra atrisinātāja kontekstā, kas ļaus serverim piekļūt pieprasījumam specifiskai informācijai, piemēram, galvenes vērtībām.

Izveidojiet MongoDB datu bāzi

Lai izveidotu datu bāzes savienojumu, vispirms izveidot MongoDB datu bāzi vai izveidojiet klasteru vietnē MongoDB Atlas. Pēc tam nokopējiet sniegto datu bāzes savienojuma URI virkni, izveidojiet a .env failu un ievadiet savienojuma virkni šādi:

MONGO_URI=""

Definējiet datu modeli

Definējiet datu modeli, izmantojot Mongoose. Izveidojiet jaunu modeļi/lietotājs.js failu un iekļaujiet šādu kodu:

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

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

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

Definējiet GraphQL shēmu

GraphQL API shēmā ir definēta to datu struktūra, kurus var vaicāt, kā arī pieejamās darbības (vaicājumi un mutācijas), ko varat veikt, lai mijiedarbotos ar datiem, izmantojot API.

Lai definētu shēmu, izveidojiet jaunu mapi sava projekta saknes direktorijā un piešķiriet tai nosaukumu graphql. Šajā mapē pievienojiet divus failus: typeDefs.js un solvers.js.

Iekš typeDefs.js failu, iekļaujiet šādu kodu:

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;

Izveidojiet GraphQL API atrisinātājus

Atrisinātāja funkcijas nosaka, kā dati tiek izgūti, reaģējot uz klienta vaicājumiem un mutācijām, kā arī citiem shēmā definētajiem laukiem. Kad klients nosūta vaicājumu vai mutāciju, GraphQL serveris aktivizē atbilstošos atrisinātājus, lai apstrādātu un atgrieztu nepieciešamos datus no dažādiem avotiem, piemēram, datu bāzēm vai API.

Lai ieviestu autentifikāciju un autorizāciju, izmantojot JSON tīmekļa marķierus (JWT), definējiet reģistra un pieteikšanās mutāciju atrisinātājus. Tie apstrādās lietotāju reģistrācijas un autentifikācijas procesus. Pēc tam izveidojiet datu iegūšanas vaicājuma risinātāju, kas būs pieejams tikai autentificētiem un autorizētiem lietotājiem.

Bet vispirms definējiet funkcijas, lai ģenerētu un pārbaudītu JWT. Iekš solvers.js failu, sāciet, pievienojot tālāk norādītos importētos datus.

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

Noteikti pievienojiet .env failam slepeno atslēgu, ko izmantosit, lai parakstītu JSON tīmekļa pilnvaras.

SECRET_KEY = '';

Lai ģenerētu autentifikācijas pilnvaru, iekļaujiet šo funkciju, kas arī norāda unikālus JWT marķiera atribūtus, piemēram, derīguma termiņu. Turklāt varat iekļaut citus atribūtus, piemēram, izdotos noteiktā laikā, pamatojoties uz jūsu īpašajām lietojumprogrammas prasībām.

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

return token;
}

Tagad ieviesiet marķiera verifikācijas loģiku, lai apstiprinātu turpmākajos HTTP pieprasījumos iekļautos JWT marķierus.

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

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

Šī funkcija izmantos marķieri kā ievadi, pārbaudīs tā derīgumu, izmantojot norādīto slepeno atslēgu, un atgriezīs dekodēto pilnvaru, ja tā ir derīga, pretējā gadījumā tiek parādīta kļūda, norādot, ka marķieris ir nederīgs.

Definējiet API atrisinātājus

Lai definētu GraphQL API atrisinātājus, jums ir jānorāda konkrētās darbības, kuras tas pārvaldīs, šajā gadījumā lietotāja reģistrācijas un pieteikšanās darbības. Vispirms izveidojiet a atrisinātāji objektu, kurā būs atrisinātāja funkcijas, pēc tam definējiet šādas mutācijas darbības:

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 reģistrēties mutācija apstrādā reģistrācijas procesu, pievienojot datubāzei jaunos lietotāja datus. Kamēr Pieslēgties mutācija pārvalda lietotāju pieteikšanos — veiksmīgas autentifikācijas gadījumā tā ģenerēs JWT marķieri, kā arī atbildē atgriezīs veiksmes ziņojumu.

Tagad iekļaujiet vaicājumu risinātāju lietotāja datu izgūšanai. Lai nodrošinātu, ka šis vaicājums būs pieejams tikai autentificētiem un autorizētiem lietotājiem, iekļaujiet autorizācijas loģiku, lai ierobežotu piekļuvi tikai lietotājiem ar Administrators lomu.

Būtībā vaicājums vispirms pārbaudīs marķiera derīgumu un pēc tam lietotāja lomu. Ja autorizācijas pārbaude ir veiksmīga, atrisinātāja vaicājums turpinās, lai izgūtu un atgrieztu lietotāju datus no datu bāzes.

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

Visbeidzot, startējiet izstrādes serveri:

node server.js

Satriecošs! Tagad turpiniet un pārbaudiet API funkcionalitāti, izmantojot Apollo Server API smilškaste savā pārlūkprogrammā. Piemēram, varat izmantot reģistrēties mutāciju, lai datubāzē pievienotu jaunus lietotāja datus, un pēc tam Pieslēgties mutācija, lai autentificētu lietotāju.

Visbeidzot, pievienojiet JWT pilnvaru autorizācijas galvenes sadaļai un turpiniet, lai datubāzē meklētu lietotāja datus.

GraphQL API nodrošināšana

Autentifikācija un autorizācija ir būtiskas GraphQL API drošības sastāvdaļas. Tomēr ir svarīgi atzīt, ka ar tiem vien var nepietikt, lai nodrošinātu visaptverošu drošību. Jums ir jāievieš papildu drošības pasākumi, piemēram, ievades validācija un sensitīvu datu šifrēšana.

Izmantojot visaptverošu drošības pieeju, jūs varat aizsargāt savus API pret dažādiem iespējamiem uzbrukumiem.