GraphQL je priljubljena alternativa tradicionalni arhitekturi API-ja RESTful, ki ponuja prilagodljiv in učinkovit jezik za poizvedovanje in manipulacijo podatkov za API-je. S svojo vse večje sprejemanje, postaja vedno bolj pomembno dati prednost varnosti API-jev GraphQL za zaščito aplikacij pred nepooblaščenim dostopom in morebitnimi podatki kršitve.
Eden od učinkovitih pristopov za zaščito API-jev GraphQL je implementacija spletnih žetonov JSON (JWT). JWT zagotavljajo varno in učinkovito metodo za odobritev dostopa do zaščitenih virov in izvajanje pooblaščenih dejanj, kar zagotavlja varno komunikacijo med odjemalci in API-ji.
Avtentikacija in avtorizacija v API-jih GraphQL
Za razliko od API-ji RESTAPI-ji GraphQL imajo običajno eno končno točko, ki odjemalcem omogoča, da v svojih poizvedbah dinamično zahtevajo različne količine podatkov. Medtem ko je ta prilagodljivost njegova prednost, povečuje tudi tveganje morebitnih varnostnih napadov, kot so ranljivosti v okvari nadzora dostopa.
Da bi zmanjšali to tveganje, je pomembno, da implementirate robustne postopke preverjanja pristnosti in avtorizacije, vključno s pravilno opredelitvijo dovoljenj za dostop. S tem zagotovite, da lahko samo pooblaščeni uporabniki dostopajo do zaščitenih virov, in na koncu zmanjšate tveganje morebitnih vdorov v varnost in izgube podatkov.
Kodo tega projekta najdete v GitHub repozitorij.
Nastavite strežnik Express.js Apollo
Strežnik Apollo je pogosto uporabljena implementacija strežnika GraphQL za API-je GraphQL. Uporabite ga lahko za enostavno izdelavo shem GraphQL, definiranje razreševalcev in upravljanje različnih virov podatkov za vaše API-je.
Če želite nastaviti strežnik Express.js Apollo, ustvarite in odprite mapo projekta:
mkdir graphql-API-jwt
cd graphql-API-jwt
Nato zaženite ta ukaz, da inicializirate nov projekt Node.js z uporabo npm, upravitelj paketov Node:
npm init --yes
Zdaj pa namestite te pakete.
npm install apollo-server graphql mongoose jsonwebtokens dotenv
Na koncu ustvarite a server.js datoteko v korenskem imeniku in nastavite strežnik s to kodo:
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);
});
Strežnik GraphQL je nastavljen z typeDefs in razreševalci parametrov, ki določajo shemo in operacije, ki jih API lahko obravnava. The kontekstu možnost konfigurira objekt req glede na kontekst vsakega razreševalnika, kar bo strežniku omogočilo dostop do podrobnosti, specifičnih za zahtevo, kot so vrednosti glave.
Ustvarite bazo podatkov MongoDB
Če želite najprej vzpostaviti povezavo z bazo podatkov ustvarite bazo podatkov MongoDB oz nastavite gručo na MongoDB Atlas. Nato kopirajte podani niz URI povezave baze podatkov, ustvarite a .env datoteko in vnesite povezovalni niz, kot sledi:
MONGO_URI=""
Definirajte podatkovni model
Definirajte podatkovni model z uporabo Mongoose. Ustvari novo modeli/uporabnik.js datoteko in vključite naslednjo kodo:
const {model, Schema} = require('mongoose');
const userSchema = new Schema({
name: String,
password: String,
role: String
});
module.exports = model('user', userSchema);
Definirajte shemo GraphQL
V API-ju GraphQL shema definira strukturo podatkov, po katerih je mogoče poizvedovati, in oris razpoložljive operacije (poizvedbe in mutacije), ki jih lahko izvajate za interakcijo s podatki prek API.
Če želite definirati shemo, ustvarite novo mapo v korenskem imeniku vašega projekta in jo poimenujte graphql. V to mapo dodajte dve datoteki: typeDefs.js in resolvers.js.
V typeDefs.js datoteko, vključite naslednjo kodo:
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;
Ustvarite razreševalce za GraphQL API
Funkcije razreševalnika določajo, kako se podatki pridobijo kot odgovor na poizvedbe in mutacije odjemalca ter druga polja, definirana v shemi. Ko odjemalec pošlje poizvedbo ali mutacijo, strežnik GraphQL sproži ustrezne razreševalce za obdelavo in vrnitev zahtevanih podatkov iz različnih virov, kot so baze podatkov ali API-ji.
Če želite implementirati avtentikacijo in avtorizacijo z uporabo spletnih žetonov JSON (JWT), definirajte razreševalce za mutacije registra in prijave. Ti bodo upravljali postopke registracije in avtentikacije uporabnikov. Nato ustvarite razreševalec poizvedb za pridobivanje podatkov, ki bo dostopen samo overjenim in pooblaščenim uporabnikom.
Najprej pa definirajte funkcije za ustvarjanje in preverjanje JWT-jev. V resolvers.js datoteko, začnite z dodajanjem naslednjih uvozov.
const User = require("../models/user");
const jwt = require('jsonwebtoken');
const secretKey = process.env.SECRET_KEY;
Ne pozabite dodati skrivnega ključa, ki ga boste uporabili za podpisovanje spletnih žetonov JSON, v datoteko .env.
SECRET_KEY = '' ;
Če želite ustvariti žeton za preverjanje pristnosti, vključite naslednjo funkcijo, ki določa tudi edinstvene atribute za žeton JWT, npr. čas poteka. Poleg tega lahko vključite druge atribute, kot so izdani ob času glede na vaše posebne zahteve aplikacije.
functiongenerateToken(user) {
const token = jwt.sign(
{ id: user.id, role: user.role },
secretKey,
{ expiresIn: '1h', algorithm: 'HS256' }
);
return token;
}
Zdaj implementirajte logiko preverjanja žetonov, da potrdite žetone JWT, vključene v naslednje zahteve HTTP.
functionverifyToken(token) {
if (!token) {
thrownewError('Token not provided');
}
try {
const decoded = jwt.verify(token, secretKey, { algorithms: ['HS256'] });
return decoded;
} catch (err) {
thrownewError('Invalid token');
}
}
Ta funkcija bo vzela žeton kot vhod, preverila njegovo veljavnost z navedenim skrivnim ključem in vrnila dekodirani žeton, če je veljaven, sicer vrže napako, ki označuje neveljaven žeton.
Definirajte API Resolverje
Če želite definirati razreševalce za GraphQL API, morate orisati specifične operacije, ki jih bo upravljal, v tem primeru operacije registracije uporabnika in prijave. Najprej ustvarite a razreševalci objekt, ki bo vseboval funkcije razreševalnika, nato definirajte naslednje 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 register mutation upravlja postopek registracije z dodajanjem novih uporabniških podatkov v bazo podatkov. Medtem ko je Vpiši se mutacija upravlja prijave uporabnikov – ob uspešnem preverjanju pristnosti ustvari žeton JWT in v odgovoru vrne sporočilo o uspehu.
Zdaj vključite razreševalec poizvedb za pridobivanje uporabniških podatkov. Če želite zagotoviti, da bo ta poizvedba dostopna samo overjenim in pooblaščenim uporabnikom, vključite avtorizacijsko logiko, da omejite dostop samo uporabnikom z skrbnik vlogo.
V bistvu bo poizvedba najprej preverila veljavnost žetona in nato uporabniško vlogo. Če je preverjanje avtorizacije uspešno, bo poizvedba razreševalnika nadaljevala s pridobivanjem in vrnitvijo podatkov uporabnikov iz baze podatkov.
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');
}
},
},
};
Končno zaženite razvojni strežnik:
node server.js
super! Zdaj pa nadaljujte in preizkusite funkcionalnost API-ja z uporabo peskovnika API-ja strežnika Apollo v vašem brskalniku. Na primer, lahko uporabite register mutacijo za dodajanje novih uporabniških podatkov v zbirko podatkov in nato Vpiši se mutacijo za avtentikacijo uporabnika.
Nazadnje dodajte žeton JWT v razdelek avtorizacijske glave in nadaljujte s poizvedovanjem baze podatkov za uporabniške podatke.
Varovanje API-jev GraphQL
Avtentikacija in avtorizacija sta ključni komponenti za zaščito API-jev GraphQL. Kljub temu se je treba zavedati, da sami morda ne bodo zadostovali za zagotavljanje celovite varnosti. Uvesti bi morali dodatne varnostne ukrepe, kot sta preverjanje vnosa in šifriranje občutljivih podatkov.
S sprejetjem celovitega varnostnega pristopa lahko svoje API-je zaščitite pred različnimi možnimi napadi.