GraphQL je oblíbená alternativa k tradiční architektuře RESTful API, která nabízí flexibilní a efektivní jazyk pro dotazy na data a manipulaci s API. S jeho s rostoucím přijetím je stále důležitější upřednostňovat zabezpečení GraphQL API, aby byly chráněny aplikace před neoprávněným přístupem a potenciálními daty. porušení.

Jedním z účinných přístupů k zabezpečení rozhraní GraphQL API je implementace webových tokenů JSON (JWT). JWT poskytují bezpečnou a efektivní metodu pro udělování přístupu k chráněným zdrojům a provádění autorizovaných akcí, zajišťující bezpečnou komunikaci mezi klienty a API.

Autentizace a autorizace v GraphQL API

Na rozdíl od REST APIRozhraní API GraphQL mají obvykle jeden koncový bod, který klientům umožňuje dynamicky vyžadovat různá množství dat ve svých dotazech. I když je tato flexibilita její silnou stránkou, zvyšuje také riziko potenciálních bezpečnostních útoků, jako jsou zranitelnosti prolomené kontroly přístupu.

Ke zmírnění tohoto rizika je důležité implementovat robustní procesy ověřování a autorizace, včetně správného definování přístupových oprávnění. Tím zaručíte, že pouze oprávnění uživatelé budou mít přístup k chráněným zdrojům, a v konečném důsledku snížíte riziko potenciálního narušení bezpečnosti a ztráty dat.

Kód tohoto projektu najdete v něm GitHub úložiště.

Nastavte Express.js Apollo Server

Server Apollo je široce používaná implementace serveru GraphQL pro rozhraní GraphQL API. Můžete jej použít ke snadnému vytváření schémat GraphQL, definování resolverů a správě různých zdrojů dat pro vaše API.

Chcete-li nastavit Express.js Apollo Server, vytvořte a otevřete složku projektu:

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

Dále spusťte tento příkaz pro inicializaci nového projektu Node.js pomocí npm, správce balíčků Node:

npm init --yes

Nyní nainstalujte tyto balíčky.

npm install apollo-server graphql mongoose jsonwebtokens dotenv

Nakonec vytvořte a server.js soubor v kořenovém adresáři a nastavte server pomocí tohoto kódu:

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

Server GraphQL je nastaven s typeDefs a řešitelé parametry, určující schéma a operace, které API dokáže zpracovat. The kontext volba konfiguruje objekt req do kontextu každého resolveru, což serveru umožní přístup k podrobnostem specifickým pro požadavek, jako jsou hodnoty záhlaví.

Vytvořte databázi MongoDB

Chcete-li navázat připojení k databázi, nejprve vytvořit databázi MongoDB nebo nastavit cluster na MongoDB Atlas. Poté zkopírujte poskytnutý řetězec URI připojení k databázi a vytvořte a .env soubor a zadejte připojovací řetězec takto:

MONGO_URI=""

Definujte datový model

Definujte datový model pomocí Mongoose. Vytvoř nový models/user.js soubor a zahrňte následující kód:

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

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

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

Definujte schéma GraphQL

V GraphQL API schéma definuje strukturu dat, na která lze dotazovat, a také dostupné operace (dotazy a mutace), které můžete provádět pro interakci s daty prostřednictvím rozhraní API.

Chcete-li definovat schéma, vytvořte novou složku v kořenovém adresáři vašeho projektu a pojmenujte ji graphql. Do této složky přidejte dva soubory: typeDefs.js a resolvers.js.

V typeDefs.js soubor, zahrňte následující kód:

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;

Vytvořte překladače pro GraphQL API

Funkce překladače určují, jak jsou data načítána v reakci na klientské dotazy a mutace, stejně jako další pole definovaná ve schématu. Když klient odešle dotaz nebo mutaci, server GraphQL spustí odpovídající překladače, aby zpracovaly a vrátily požadovaná data z různých zdrojů, jako jsou databáze nebo rozhraní API.

Chcete-li implementovat ověřování a autorizaci pomocí webových tokenů JSON (JWT), definujte resolvery pro mutace registru a přihlášení. Ty se budou starat o procesy registrace a ověřování uživatelů. Poté vytvořte překladač dotazů načítání dat, který bude přístupný pouze ověřeným a autorizovaným uživatelům.

Nejprve však definujte funkce pro generování a ověřování JWT. V resolvers.js souboru, začněte přidáním následujících importů.

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

Ujistěte se, že jste do souboru .env přidali tajný klíč, který budete používat k podepisování webových tokenů JSON.

SECRET_KEY = '';

Chcete-li vygenerovat ověřovací token, zahrňte následující funkci, která také určuje jedinečné atributy pro token JWT, např. čas vypršení platnosti. Kromě toho můžete začlenit další atributy, například vydané v čase, na základě vašich specifických požadavků aplikace.

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

return token;
}

Nyní implementujte logiku ověření tokenu k ověření tokenů JWT zahrnutých v následných požadavcích 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');
}
}

Tato funkce vezme token jako vstup, ověří jeho platnost pomocí zadaného tajného klíče a vrátí dekódovaný token, pokud je platný, jinak vyvolá chybu označující neplatný token.

Definujte rozhraní API

Chcete-li definovat resolvery pro GraphQL API, musíte nastínit konkrétní operace, které bude spravovat, v tomto případě operace registrace uživatele a přihlášení. Nejprve vytvořte a řešitelé objekt, který bude obsahovat funkce resolveru, pak definujte následující operace mutace:

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 Registrovat mutace zpracovává registrační proces přidáním nových uživatelských dat do databáze. Zatímco přihlásit se mutace spravuje přihlášení uživatelů – při úspěšné autentizaci vygeneruje token JWT a v odpovědi vrátí zprávu o úspěchu.

Nyní zahrňte překladač dotazů pro načítání uživatelských dat. Chcete-li zajistit, že tento dotaz bude přístupný pouze ověřeným a autorizovaným uživatelům, zahrňte autorizační logiku, která omezí přístup pouze na uživatele s Admin role.

Dotaz v podstatě nejprve zkontroluje platnost tokenu a poté uživatelskou roli. Pokud je kontrola autorizace úspěšná, dotaz překladače bude pokračovat v načítání a vracení dat uživatelů z databáze.

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

Nakonec spusťte vývojový server:

node server.js

Skvělý! Nyní pokračujte a otestujte funkčnost API pomocí karantény Apollo Server API ve vašem prohlížeči. Můžete například použít Registrovat mutace pro přidání nových uživatelských dat do databáze a poté, přihlásit se mutace k ověření uživatele.

Nakonec přidejte token JWT do sekce záhlaví autorizace a pokračujte v dotazování databáze na uživatelská data.

Zabezpečení rozhraní GraphQL API

Autentizace a autorizace jsou klíčové komponenty pro zabezpečení GraphQL API. Je však důležité si uvědomit, že samy o sobě nemusí být dostatečné k zajištění komplexní bezpečnosti. Měli byste implementovat další bezpečnostní opatření, jako je ověřování vstupu a šifrování citlivých dat.

Přijetím komplexního bezpečnostního přístupu můžete ochránit svá rozhraní API před různými potenciálními útoky.