Hur man autentiserar och auktoriserar användare med JWT i NodeJS

Autentisering och auktorisering är grundkonceptet för datorsäkerhet. Du använder dina referenser (som ett användarnamn och lösenord) för att bevisa din identitet och identifiera dig som en registrerad användare och sedan få ytterligare privilegier.

Detta gäller även när du loggar in på onlinetjänster med ditt Facebook- eller Google-konto.

I den här artikeln kommer vi att bygga ett Nodejs API med JWT (JSON Web Tokens) autentisering. Verktygen som vi kommer att använda i denna handledning är:

  • Expressjs
  • MongoDB databas
  • Mungo
  • Dotenv
  • Bcryptjs
  • Jsonwebtoken

Autentisering vs. Tillstånd

Vad är autentisering?

Autentisering är processen att identifiera användare genom att skaffa referenser som e-post, lösenord och tokens. De givna referenserna jämförs med den registrerade användarens autentiseringsuppgifter, som är tillgängliga i filen för det lokala datorsystemet eller eventuella databaser. Om de givna autentiseringsuppgifterna stämmer överens med tillgängliga data i databasen, slutförs autentiseringsprocessen och användaren tillåts komma åt resurserna.

Vad är auktorisering?

Auktorisering sker efter autentisering. Varje auktorisering måste ha en autentiseringsprocess. Det är processen att tillåta användare att komma åt resurser från systemen eller en webbplats. I den här handledningen kommer vi att tillåta inloggade användare att komma åt användarens data. Om användaren inte är inloggad kommer de inte att kunna använda åtkomst till data.

De bästa exemplen på auktorisering är sociala medieplattformar som Facebook och Twitter. Du kan inte komma åt innehåll på sociala medier utan att ha ett konto.

Ett annat exempel på auktorisering är prenumerationsbaserat innehåll, din autentisering kan göras genom att logga in på webbplatsen, men du kommer inte att få tillgång till innehållet förrän du inte har prenumererat.

Nödvändig förutsättning

Innan du går vidare antar jag att du har en grundläggande förståelse för Javascript och MongoDB och goda kunskaper om Nodejs.

Se till att du har installerat nod och npm på din lokala dator. För att kontrollera om nod och npm är installerade på din dator, öppna kommandotolken och skriv node -v och npm -v. Detta bör visa följande resultat.

Dina versioner kan skilja sig från mina. NPM laddas automatiskt ned med noden. Om du inte har laddat ner det ännu, ladda ner det från NodeJS webbplats.

Du behöver en IDE (Integrated Development Environment) för att skriva kod. I den här handledningen använder jag VS-kodredigerare. Om du har en till kan du använda den också. Om du inte har någon IDE installerad på din dator kan du ladda ner den från Visual Studio hemsida. Ladda ner den baserat på ditt lokala system.

Projektuppsättning

Skapa ett mappnamn nodeapi var som helst på din lokala dator och öppna den sedan med vs-code. Öppna vs-code-terminalen och initiera sedan nodpakethanteraren genom att skriva.

npm init -y

Se till att du är på nodeapi-katalogen.

Kommandot ovan kommer att skapa en package.json-fil som innehåller alla beroenden som vi kommer att använda i det här projektet.

Nu ska vi ladda ner alla paket som nämns ovan, skriv nu och skriv in dem i terminalen.

npm install express dotenv jsonwebtoken mongoose bcryptjs

Nu kommer du att ha filer och mappar, som visas nedan.

Skapa server och ansluta databas

Skapa nu en fil med namnet index.js och en mapp med namnet config. Inuti config, skapa två filer med namnet conn.js för att ansluta till databasen och config.env för att deklarera miljövariabler. Skriv den givna koden nedan i respektive filer.

index.js

const express = require('express');
const dotenv = require('dotenv');

//Configure dotenv files above using any other library and files
dotenv.config({path:'./config/config.env'}); 

//Creating an app from express
const app = express();

//Using express.json to get request of json data
app.use(express.json());



//listening to the server
app.listen(process.env.PORT,()=>{
    console.log(`Server is listening at ${process.env.PORT}`);
})

Om du använder dotenv, konfigurera den i din index.js-fil innan du anropar en annan fil som använder miljövariabler.

conn.js

const mongoose = require('mongoose');

mongoose.connect(process.env.URI, 
    { useNewUrlParser: true,
     useUnifiedTopology: true })
    .then((data) => {
        console.log(`Database connected to ${data.connection.host}`)
})

config.env

URI = 'mongodb+srv://ghulamrabbani883:[email protected]/?retryWrites=true&w=majority'
PORT = 5000

Jag använder mongo-DB Atlas URI, du kan också använda localhost.

Skapa modeller och rutter

Modell är en layout av dina data i Mongo-DB-databasen och kommer att lagras som ett JSON-dokument. För att skapa en modell kommer vi att använda mongoose-schemat.

Routing hänvisar till hur en applikation svarar på klientförfrågningar. Vi kommer att använda expressrouterfunktionen för att skapa rutter.

Routingmetoder tar vanligtvis två argument. Den första är rutt, och den andra är återuppringningsfunktionen för att definiera vad denna rutt skulle göra på kundens begäran.

Det tar också ett tredje argument som en middleware-funktion när det behövs, som i autentiseringsprocessen. När vi bygger ett autentiserat API kommer vi också att använda funktionen mellanprogram för att auktorisera och autentisera användare.

Nu ska vi skapa två mappar som heter rutter och modeller. Inuti rutter, skapa ett filnamn userRoute.js och inuti mappen models, skapa ett filnamn userModel.js. När du har skapat filer, skriv följande kod i respektive filer.

userModel.js

const mongoose = require('mongoose');

//Creating Schema using mongoose
const userSchema = new mongoose.Schema({
    name: {
        type:String,
        required:true,
        minLength:[4,'Name should be minimum of 4 characters']
    },
    email:{
        type:String,
        required:true,
        unique:true,
    },
    password:{
        type:String,
        required:true,
        minLength:[8,'Password should be minimum of 8 characters']
    },
    token:{
        type:String
    }
})

//Creating models
const userModel = mongoose.model('user',userSchema);
module.exports = userModel;

userRoute.js

const express = require('express');
//Creating express router
const route = express.Router();
//Importing userModel
const userModel = require('../models/userModel');

//Creating register route
route.post('/register',(req,res)=>{

})
//Creating login routes
route.post('/login',(req,res)=>{

})

//Creating user routes to fetch users data
route.get('/user',(req,res)=>{

})

Implementera ruttfunktionalitet och skapa JWT-tokens

Vad är JWT?

JSON webbtokens (JWT) är ett javascript-bibliotek som skapar och verifierar tokens. Det är en öppen standard som används för att dela information mellan två parter – en klient och en server. Vi kommer att använda två funktioner i JWT. Den första funktionen är tecken för att skapa en ny token och den andra funktionen är verifiering för att verifiera token.

Vad är bcryptjs?

Bcryptjs är en hashfunktion skapad av Niels Provos och David Mazières. Den använder en hash-algoritm för att hasha lösenordet. Den har två vanligaste funktioner som vi kommer att använda i det här projektet. Den första bcryptjs-funktionen är hash för att generera hashvärde och den andra funktionen är jämförelsefunktion för att jämföra lösenord.

Implementera ruttfunktionalitet

Callback-funktionen i routing tar tre argument, begäran, svar och nästa funktion. Nästa argument är valfritt; skicka detta bara när du behöver det. Dessa argument bör finnas i begäran, svaret och nästa ordning. Ändra nu filerna userRoute.js, config.env och index.js med följande koder.

userRoute.js

//Requiring all the necessary files and libraries
const express = require('express');
const bcrypt = require('bcryptjs');
const jwt = require('jsonwebtoken');

//Creating express router
const route = express.Router();
//Importing userModel
const userModel = require('../models/userModel');

//Creating register route
route.post("/register", async (req, res) => {

    try {
        const { name, email, password } = req.body;
        //Check emptyness of the incoming data
        if (!name || !email || !password) {
            return res.json({ message: 'Please enter all the details' })
        }

        //Check if the user already exist or not
        const userExist = await userModel.findOne({ email: req.body.email });
        if (userExist) {
            return res.json({ message: 'User already exist with the given emailId' })
        }
        //Hash the password
        const salt = await bcrypt.genSalt(10);
        const hashPassword = await bcrypt.hash(req.body.password, salt);
        req.body.password = hashPassword;
        const user = new userModel(req.body);
        await user.save();
        const token = await jwt.sign({ id: user._id }, process.env.SECRET_KEY, {
            expiresIn: process.env.JWT_EXPIRE,
        });
        return res.cookie({ 'token': token }).json({ success: true, message: 'User registered successfully', data: user })
    } catch (error) {
        return res.json({ error: error });
    }

})
//Creating login routes
route.post('/login', async (req, res) => {
    try {
        const { email, password } = req.body;
        //Check emptyness of the incoming data
        if (!email || !password) {
            return res.json({ message: 'Please enter all the details' })
        }
        //Check if the user already exist or not
        const userExist = await userModel.findOne({email:req.body.email});
        if(!userExist){
            return res.json({message:'Wrong credentials'})
        }
        //Check password match
        const isPasswordMatched = await bcrypt.compare(password,userExist.password);
        if(!isPasswordMatched){
            return res.json({message:'Wrong credentials pass'});
        }
        const token = await jwt.sign({ id: userExist._id }, process.env.SECRET_KEY, {
            expiresIn: process.env.JWT_EXPIRE,
        });
        return res.cookie({"token":token}).json({success:true,message:'LoggedIn Successfully'})
    } catch (error) {
        return res.json({ error: error });
    }

})

//Creating user routes to fetch users data
route.get('/user', async (req, res) => {
    try {
        const user  = await userModel.find();
        if(!user){
            return res.json({message:'No user found'})
        }
        return res.json({user:user})
    } catch (error) {
        return res.json({ error: error });  
    }
})

module.exports = route;

Om du använder Async-funktionen, använd try-catch-blocket, annars kommer det att ge ett obehandlat löftesavvisningsfel.

config.env

URI = 'mongodb+srv://ghulamrabbani883:[email protected]/?retryWrites=true&w=majority'
PORT = 5000
SECRET_KEY = KGGK>HKHVHJVKBKJKJBKBKHKBMKHB
JWT_EXPIRE = 2d

index.js

const express = require('express');
const dotenv = require('dotenv');

//Configure dotenv files above using any other library and files
dotenv.config({path:'./config/config.env'}); 
require('./config/conn');
//Creating an app from express
const app = express();
const route = require('./routes/userRoute');

//Using express.json to get request of json data
app.use(express.json());
//Using routes

app.use('/api', route);

//listening to the server
app.listen(process.env.PORT,()=>{
    console.log(`Server is listening at ${process.env.PORT}`);
})

Skapar mellanprogram för att autentisera användare

Vad är middleware?

Middleware är en funktion som har åtkomst till begäran, svarsobjekt och nästa funktion i begäran-svar-cykeln. Nästa funktion anropas när funktionsexekveringen är klar. Som jag nämnde ovan, använd next() när du måste köra en annan callback-funktion eller middleware-funktion.

Skapa nu en mapp som heter middleware, och inuti den, skapa filnamnet som auth.js och skriv följande kod.

auth.js

const userModel = require('../models/userModel');
const jwt = require('jsonwebtoken');
const isAuthenticated = async (req,res,next)=>{
    try {
        const {token} = req.cookies;
        if(!token){
            return next('Please login to access the data');
        }
        const verify = await jwt.verify(token,process.env.SECRET_KEY);
        req.user = await userModel.findById(verify.id);
        next();
    } catch (error) {
       return next(error); 
    }
}

module.exports = isAuthenticated;

Installera nu cookie-parser-biblioteket för att konfigurera cookieParser i din app. cookieParser hjälper dig att komma åt token som lagras i cookien. Om du inte har cookieParser konfigurerad i din nodejs-app, kommer du inte att kunna komma åt cookies från rubrikerna för förfrågningsobjektet. Skriv nu i terminalen för att ladda ner cookie-parser.

npm i cookie-parser

Nu har du en cookieParser installerad. Konfigurera din app genom att ändra filen index.js och lägg till middleware till rutten ”/user/”.

index.js-filen

const cookieParser = require('cookie-parser');
const express = require('express');
const dotenv = require('dotenv');

//Configure dotenv files above using any other library and files
dotenv.config({path:'./config/config.env'}); 
require('./config/conn');
//Creating an app from express
const app = express();
const route = require('./routes/userRoute');

//Using express.json to get request of json data
app.use(express.json());
//Configuring cookie-parser
app.use(cookieParser()); 

//Using routes
app.use('/api', route);

//listening to the server
app.listen(process.env.PORT,()=>{
    console.log(`Server is listening at ${process.env.PORT}`);
})

userRoute.js

//Requiring all the necessary files and libraries
const express = require('express');
const bcrypt = require('bcryptjs');
const jwt = require('jsonwebtoken');
const isAuthenticated = require('../middleware/auth');

//Creating express router
const route = express.Router();
//Importing userModel
const userModel = require('../models/userModel');

//Creating user routes to fetch users data
route.get('/user', isAuthenticated, async (req, res) => {
    try {
        const user = await userModel.find();
        if (!user) {
            return res.json({ message: 'No user found' })
        }
        return res.json({ user: user })
    } catch (error) {
        return res.json({ error: error });
    }
})

module.exports = route;

Rutten ”/användare” är endast tillgänglig när användaren är inloggad.

Kontrollerar API:erna på POSTMAN

Innan du kontrollerar API:er måste du ändra filen package.json. Lägg till följande kodrader.

"scripts": {
    "test": "echo "Error: no test specified" && exit 1",
    "start": "node index.js",
    "dev": "nodemon index.js"
  },

Du kan starta servern genom att skriva npm start, men den kommer bara att köras en gång. För att hålla din server igång medan du byter filer behöver du nodemon. Ladda ner den genom att skriva i terminalen

npm install -g nodemon

-g-flaggan laddar ner nodemonen globalt på ditt lokala system. Du behöver inte ladda ner den igen och igen för varje nytt projekt.

För att köra servern, skriv npm run dev i terminalen. Du får följande resultat.

Äntligen är din kod färdig, och servern körs korrekt, gå till postman och kontrollera om den fungerar.

Vad är POSTMAN?

POSTMAN är ett mjukvaruverktyg för att designa, bygga, utveckla och testa API.

Om du inte har laddat ner brevbäraren till din dator, ladda ner den från postman hemsida.

Öppna nu brevbäraren och skapa ett samlingsnamn nodeAPItest, och inuti det, skapa tre förfrågningar: registrera, logga in och användare. Du bör ha följande filer.

När du skickar JSON-data till ”localhost:5000/api/register” får du följande resultat.

Eftersom vi också skapar och sparar tokens i cookies under registreringen, kan du få användarinformationen när du begär rutten ”localhost:5000/api/user”. Du kan kontrollera resten av förfrågningarna på POSTMAN.

Om du vill ha den fullständiga koden kan du få den från min github-konto.

Slutsats

I den här handledningen har vi lärt oss hur man tillämpar autentisering på NodeJS API med JWT-tokens. Vi har också auktoriserat användare att komma åt användardata.

GLAD KODNING!