0% ont trouvé ce document utile (0 vote)
55 vues23 pages

Application Node.js avec JWT et MySQL

Transféré par

Takoua Mohamed
Copyright
© © All Rights Reserved
Nous prenons très au sérieux les droits relatifs au contenu. Si vous pensez qu’il s’agit de votre contenu, signalez une atteinte au droit d’auteur ici.
Formats disponibles
Téléchargez aux formats DOCX, PDF, TXT ou lisez en ligne sur Scribd
0% ont trouvé ce document utile (0 vote)
55 vues23 pages

Application Node.js avec JWT et MySQL

Transféré par

Takoua Mohamed
Copyright
© © All Rights Reserved
Nous prenons très au sérieux les droits relatifs au contenu. Si vous pensez qu’il s’agit de votre contenu, signalez une atteinte au droit d’auteur ici.
Formats disponibles
Téléchargez aux formats DOCX, PDF, TXT ou lisez en ligne sur Scribd

Atelier 07

Voici la structure de répertoires de notre application Node.js Express :

– config

 configure MySQL database & Sequelize


 configure Auth Key

– routes

 auth.routes.js: POST signup & signin


 user.routes.js: GET public & protected resources

– middlewares

 verifySignUp.js: check duplicate Username or Email


 authJwt.js: verify Token, check User roles in database

– controllers

 auth.controller.js: handle signup & signin actions

1
 user.controller.js: return public & protected content

– models for Sequelize Models

 user.model.js
 role.model.js

– server.js: import and initialize necessary modules and routes, listen for connections.

Créer une application Node.js


Tout d'abord, nous créons un dossier pour notre projet :

$ mkdir node-js-jwt-auth

$ cd node-js-jwt-auth

Ensuite, nous initialisons l'application Node.js avec un fichier package.json :

npm init

name: (node-js-jwt-auth)

version: (1.0.0)

description: Node.js Demo for JWT Authentication

entry point: (index.js) server.js

test command:

git repository:

keywords: node.js, express, jwt, authentication, mysql

author: bezkoder

license: (ISC)

Is this ok? (yes) yes

2
Nous devons installer les modules nécessaires : express, cors, sequelize, et . Exécutez la
commande :mysql2jsonwebtokenbcryptjs:

npm install express sequelize mysql2 cors jsonwebtoken bcryptjs --save

Le fichier package.json ressemble maintenant à ceci :


{
"name": "node-js-jwt-auth",
"version": "1.0.0",
"description": "Node.js Demo for JWT Authentication",
"main": "server.js",
"scripts": {
"test": "echo \"Error: no test specified\" && exit 1"
},
"keywords": [
"node.js",
"jwt",
"authentication",
"express",
"mysql"
],
"author": "bezkoder",
"license": "ISC",
"dependencies": {
"bcryptjs": "^2.4.3",
"cors": "^2.8.5",
"express": "^4.18.2",
"jsonwebtoken": "^9.0.0",
"mysql2": "^2.3.3",
"sequelize": "^6.32.1"
}
}

Configurer le serveur Web Express


Dans le dossier racine, créons un nouveau fichier server.js :
const express = require("express");
const cors = require("cors");

3
const app = express();

var corsOptions = {
origin: "http://localhost:8081"
};

app.use(cors(corsOptions));

// parse requests of content-type - application/json


app.use(express.json());

// parse requests of content-type - application/x-www-form-urlencoded


app.use(express.urlencoded({ extended: true }));

// simple route
app.get("/", (req, res) => {
res.json({ message: "Welcome to bezkoder application." });
});

// set port, listen for requests


const PORT = process.env.PORT || 8080;
app.listen(PORT, () => {
console.log(`Server is running on port ${PORT}.`);
});
Laissez-moi vous expliquer ce que nous venons de faire :
– import expresset corsmodules :

 Express sert à construire les API Rest


 cors fournit un middleware Express pour activer CORS

– créez une application Express, puis ajoutez un analyseur de corps de requête et corsdes
middlewares à l’aide app.use()de la méthode. Notez que nous définissons
origin: http://localhost:8081.
– définir une route GET simple à tester.
– écoutez sur le port 8080 les demandes entrantes.

Lançons maintenant l'application avec la commande : node server.js.


Ouvrez votre navigateur avec l'url http://localhost:8080/ , vous verrez :

4
Configure MySQL database & Sequelize
In the app folder, create config folder for configuration with db.config.js file like this:
module.exports = {
HOST: "localhost",
USER: "root",
PASSWORD: "123456",
DB: "testdb",
dialect: "mysql",
pool: {
max: 5,
min: 0,
acquire: 30000,
idle: 10000
}
};
Les cinq premiers paramètres concernent la connexion MySQL.
poolest facultatif, il sera utilisé pour la configuration du pool de connexions Sequelize :

 max: nombre maximum de connexion dans le pool


 min: nombre minimum de connexion dans le pool
 idle: durée maximale, en millisecondes, pendant laquelle une connexion peut être
inactive avant d'être libérée
 acquire : durée maximale, en millisecondes, pendant laquelle ce pool tentera
d'établir une connexion avant de générer une erreur

Définir le modèle Sequelize


Dans le dossier models , créez Userun Rolemodèle de données comme le code suivant :

models/user.model.js
module.exports = (sequelize, Sequelize) => {
const User = sequelize.define("users", {
username: {
type: Sequelize.STRING

5
},
email: {
type: Sequelize.STRING
},
password: {
type: Sequelize.STRING
}
});

return User;
};
models/role.model.js
module.exports = (sequelize, Sequelize) => {
const Role = sequelize.define("roles", {
id: {
type: Sequelize.INTEGER,
primaryKey: true
},
name: {
type: Sequelize.STRING
}
});

return Role;
};
Ces modèles Sequelize représentent la table des utilisateurs et des rôles dans la base de
données MySQL.

Après avoir initialisé Sequelize, nous n'avons pas besoin d'écrire des fonctions CRUD,
Sequelize les prend toutes en charge :

 créez un nouvel utilisateur :create(object)


 trouver un utilisateur par identifiant :findByPk(id)
 trouver un utilisateur par email :findOne({ where: { email: ... } })
 obtenir tous les utilisateurs :findAll()
 trouver tous les utilisateurs par nom d'utilisateur :findAll({ where:
{ username: ... } })

Ces fonctions seront utilisées dans nos contrôleurs et middlewares.

6
Initialiser Sequelize
Créez maintenant app / models / index.js avec un contenu comme celui-ci :
const config = require("../config/db.config.js");

const Sequelize = require("sequelize");


const sequelize = new Sequelize(
config.DB,
config.USER,
config.PASSWORD,
{
host: config.HOST,
dialect: config.dialect,
pool: {
max: config.pool.max,
min: config.pool.min,
acquire: config.pool.acquire,
idle: config.pool.idle
}
}
);

const db = {};

db.Sequelize = Sequelize;
db.sequelize = sequelize;

db.user = require("../models/user.model.js")(sequelize, Sequelize);


db.role = require("../models/role.model.js")(sequelize, Sequelize);

db.role.belongsToMany(db.user, {
through: "user_roles"
});
db.user.belongsToMany(db.role, {
through: "user_roles"
});

db.ROLES = ["user", "admin", "moderator"];

7
module.exports = db;
L'association entre les utilisateurs et les rôles est une relation plusieurs-à-plusieurs :
– Un utilisateur peut avoir plusieurs rôles.
– Un rôle peut être assumé par plusieurs utilisateurs.

Nous utilisons User.belongsToMany(Role)pour indiquer que le modèle utilisateur peut


appartenir à plusieurs Role s et vice versa.

Avec through, nous allons avoir une nouvelle table user_roles comme connexion entre les
utilisateurs et la table des rôles via leur clé primaire comme clés étrangères.

N'oubliez pas d'appeler sync()la méthode dans server.js .


...
const app = express();
app.use(...);

const db = require("./app/models");
const Role = db.role;

db.sequelize.sync({force: true}).then(() => {


console.log('Drop and Resync Db');
initial();
});

...
function initial() {
Role.create({
id: 1,
name: "user"
});

Role.create({
id: 2,
name: "moderator"
});

Role.create({
id: 3,
name: "admin"
});

8
}
initial()La fonction nous aide à créer 3 lignes dans la base de données.
En développement, vous devrez peut-être supprimer les tables existantes et resynchroniser
la base de données. Vous pouvez donc utiliser force: truele code ci-dessus.

Pour la production, insérez simplement ces lignes manuellement et utilisez- sync()les sans
paramètres pour éviter de perdre des données :
...
const app = express();
app.use(...);

const db = require("./app/models");

db.sequelize.sync();
...

Configurer la clé d'authentification


jsonwebtoken fonctionne comme verify()ou sign()utilise un algorithme qui nécessite
une clé secrète (sous forme de chaîne) pour encoder et décoder le jeton.

Dans le dossier app / config , créez le fichier auth.config.js avec le code suivant :
module.exports = {
secret: "bezkoder-secret-key"
};
Vous pouvez créer votre propre secretchaîne.

Créer des fonctions Middleware


Pour vérifier une action d'inscription, nous avons besoin de 2 fonctions :
– vérifier si usernameou emailest en double ou non
– vérifier si rolesla demande existe ou non

middleware/verifySignUp.js
const db = require("../models");
const ROLES = db.ROLES;
const User = db.user;

checkDuplicateUsernameOrEmail = (req, res, next) => {


// Username
User.findOne({
where: {
username: req.body.username

9
}
}).then(user => {
if (user) {
res.status(400).send({
message: "Failed! Username is already in use!"
});
return;
}

// Email
User.findOne({
where: {
email: req.body.email
}
}).then(user => {
if (user) {
res.status(400).send({
message: "Failed! Email is already in use!"
});
return;
}

next();
});
});
};

checkRolesExisted = (req, res, next) => {


if (req.body.roles) {
for (let i = 0; i < req.body.roles.length; i++) {
if (!ROLES.includes(req.body.roles[i])) {
res.status(400).send({
message: "Failed! Role does not exist = " + req.body.roles[i]
});
return;
}
}
}

10
next();
};

const verifySignUp = {
checkDuplicateUsernameOrEmail: checkDuplicateUsernameOrEmail,
checkRolesExisted: checkRolesExisted
};

module.exports = verifySignUp;

Pour traiter l'authentification et l'autorisation, nous disposons de ces fonctions :


- vérifier si cela tokenest fourni, légal ou non. Nous obtenons le jeton du x-access-token des
en-têtes HTTP, puis utilisons la fonction de jsonwebtokenverify() .
- Vérifiez si rolesl'utilisateur contient le rôle requis ou non. middleware/authJwt.js
const jwt = require("jsonwebtoken");
const config = require("../config/auth.config.js");
const db = require("../models");
const User = db.user;

verifyToken = (req, res, next) => {


let token = req.headers["x-access-token"];

if (!token) {
return res.status(403).send({
message: "No token provided!"
});
}

jwt.verify(token,
config.secret,
(err, decoded) => {
if (err) {
return res.status(401).send({
message: "Unauthorized!",
});
}
req.userId = decoded.id;

11
next();
});
};

isAdmin = (req, res, next) => {


User.findByPk(req.userId).then(user => {
user.getRoles().then(roles => {
for (let i = 0; i < roles.length; i++) {
if (roles[i].name === "admin") {
next();
return;
}
}

res.status(403).send({
message: "Require Admin Role!"
});
return;
});
});
};

isModerator = (req, res, next) => {


User.findByPk(req.userId).then(user => {
user.getRoles().then(roles => {
for (let i = 0; i < roles.length; i++) {
if (roles[i].name === "moderator") {
next();
return;
}
}

res.status(403).send({
message: "Require Moderator Role!"
});
});
});
};

12
isModeratorOrAdmin = (req, res, next) => {
User.findByPk(req.userId).then(user => {
user.getRoles().then(roles => {
for (let i = 0; i < roles.length; i++) {
if (roles[i].name === "moderator") {
next();
return;
}

if (roles[i].name === "admin") {


next();
return;
}
}

res.status(403).send({
message: "Require Moderator or Admin Role!"
});
});
});
};

const authJwt = {
verifyToken: verifyToken,
isAdmin: isAdmin,
isModerator: isModerator,
isModeratorOrAdmin: isModeratorOrAdmin
};
module.exports = authJwt;
middleware/index.js
const authJwt = require("./authJwt");
const verifySignUp = require("./verifySignUp");

module.exports = {
authJwt,
verifySignUp
};

13
Créer des contrôleurs
Contrôleur pour l'authentification
Il existe 2 fonctions principales pour l'authentification :
-signup : créer un nouvel utilisateur dans la base de données (le rôle est l'utilisateur si le
rôle n'est pas spécifié)
-signin :

 retrouver usernamela requête dans la base de données, si elle existe


 comparer passwordavec passworddans la base de données en utilisant bcrypt , si
c'est correct
 générer un jeton en utilisant jsonwebtoken
 renvoyer les informations utilisateur et accéder au jeton

controllers/auth.controller.js
const db = require("../models");
const config = require("../config/auth.config");
const User = db.user;
const Role = db.role;

const Op = db.Sequelize.Op;

var jwt = require("jsonwebtoken");


var bcrypt = require("bcryptjs");

exports.signup = (req, res) => {


// Save User to Database
User.create({
username: req.body.username,
email: req.body.email,
password: bcrypt.hashSync(req.body.password, 8)
})
.then(user => {
if (req.body.roles) {
Role.findAll({
where: {
name: {
[Op.or]: req.body.roles
}
}

14
}).then(roles => {
user.setRoles(roles).then(() => {
res.send({ message: "User was registered successfully!" });
});
});
} else {
// user role = 1
user.setRoles([1]).then(() => {
res.send({ message: "User was registered successfully!" });
});
}
})
.catch(err => {
res.status(500).send({ message: err.message });
});
};

exports.signin = (req, res) => {


User.findOne({
where: {
username: req.body.username
}
})
.then(user => {
if (!user) {
return res.status(404).send({ message: "User Not found." });
}

var passwordIsValid = bcrypt.compareSync(


req.body.password,
user.password
);

if (!passwordIsValid) {
return res.status(401).send({
accessToken: null,
message: "Invalid Password!"
});

15
}

const token = jwt.sign({ id: user.id },


config.secret,
{
algorithm: 'HS256',
allowInsecureKeySizes: true,
expiresIn: 86400, // 24 hours
});

var authorities = [];


user.getRoles().then(roles => {
for (let i = 0; i < roles.length; i++) {
authorities.push("ROLE_" + roles[i].name.toUpperCase());
}
res.status(200).send({
id: user.id,
username: user.username,
email: user.email,
roles: authorities,
accessToken: token
});
});
})
.catch(err => {
res.status(500).send({ message: err.message });
});
};

Contrôleur pour les tests Autorisation


Il existe 4 fonctions :
– /api/test/allpour l'accès public
– /api/test/userpour les utilisateurs connectés
(rôle : utilisateur / modérateur / administrateur )
– /api/test/modpour les utilisateurs ayant le rôle de modérateur
– /api/test/adminpour les utilisateurs ayant le rôle d'administrateur

controllers/user.controller.js
exports.allAccess = (req, res) => {
res.status(200).send("Public Content.");

16
};

exports.userBoard = (req, res) => {


res.status(200).send("User Content.");
};

exports.adminBoard = (req, res) => {


res.status(200).send("Admin Content.");
};

exports.moderatorBoard = (req, res) => {


res.status(200).send("Moderator Content.");
};

Définir des itinéraires


Lorsqu'un client envoie une requête pour un point de terminaison à l'aide d'une requête
HTTP (GET, POST, PUT, DELETE), nous devons déterminer comment le serveur répondra en
configurant les routes.

Nous pouvons séparer nos itinéraires en 2 parties : pour l'authentification et pour


l'autorisation (accès aux ressources protégées).

Authentification:

 POSTE/api/auth/signup
 POSTE/api/auth/signin

routes/auth.routes.js
const { verifySignUp } = require("../middleware");
const controller = require("../controllers/auth.controller");

module.exports = function(app) {
app.use(function(req, res, next) {
res.header(
"Access-Control-Allow-Headers",
"x-access-token, Origin, Content-Type, Accept"
);
next();
});

17
app.post(
"/api/auth/signup",
[
verifySignUp.checkDuplicateUsernameOrEmail,
verifySignUp.checkRolesExisted
],
controller.signup
);

app.post("/api/auth/signin", controller.signin);
};
Autorisation:

 OBTENIR/api/test/all
 GET /api/test/userpour les utilisateurs connectés
(utilisateur/modérateur/administrateur)
 GET /api/test/modpour le modérateur
 OBTENIR /api/test/adminpour l'administrateur

routes/user.routes.js
const { authJwt } = require("../middleware");
const controller = require("../controllers/user.controller");

module.exports = function(app) {
app.use(function(req, res, next) {
res.header(
"Access-Control-Allow-Headers",
"x-access-token, Origin, Content-Type, Accept"
);
next();
});

app.get("/api/test/all", controller.allAccess);

app.get(
"/api/test/user",
[authJwt.verifyToken],
controller.userBoard
);

18
app.get(
"/api/test/mod",
[authJwt.verifyToken, authJwt.isModerator],
controller.moderatorBoard
);

app.get(
"/api/test/admin",
[authJwt.verifyToken, authJwt.isAdmin],
controller.adminBoard
);
};
N'oubliez pas d'ajouter ces routes dans server.js :
...
// routes
require('./app/routes/auth.routes')(app);
require('./app/routes/user.routes')(app);

// set port, listen for requests


...

Exécuter et tester avec les résultats


Exécutez l'application Node.js avec la commande :node server.js

Enregistrez certains utilisateurs avec /signupl'API :

 administrateur avec adminrôle


 mod avec moderatoret userrôles
 zkoder avec userrôle

19
Accéder à la ressource publique : GET/api/test/all

Accéder à la ressource protégée : GET/api/test/user

20
Connectez-vous à un compte (avec un mauvais mot de passe) : POST/api/auth/signin

Connectez-vous à un compte : POST/api/auth/signin

21
Accéder aux ressources protégées : GET/api/test/user

22
23

Vous aimerez peut-être aussi