Sådan godkendes og autoriseres bruger ved hjælp af JWT i NodeJS

Autentificering og autorisation er det grundlæggende koncept for computersikkerhed. Du bruger dine legitimationsoplysninger (såsom et brugernavn og adgangskode) for at bevise din identitet og identificere dig selv som en registreret bruger og derefter få yderligere privilegier.

Dette gælder også, når du logger på onlinetjenester ved hjælp af dine Facebook- eller Google-konti.

I denne artikel skal vi bygge en Nodejs API med JWT (JSON Web Tokens) godkendelse. De værktøjer, vi skal bruge i denne tutorial er:

  • Expressjs
  • MongoDB database
  • Mongoose
  • Dotenv
  • Bcryptjs
  • Jsonwebtoken

Autentificering vs. Bemyndigelse

Hvad er godkendelse?

Autentificering er processen med at identificere brugere ved at erhverve legitimationsoplysninger som e-mail, adgangskode og tokens. De givne legitimationsoplysninger sammenlignes med den registrerede brugers legitimationsoplysninger, som er tilgængelige i filen på det lokale computersystem eller eventuelle databaser. Hvis de givne legitimationsoplysninger stemmer overens med de tilgængelige data i databasen, fuldføres godkendelsesprocessen, og brugeren får adgang til ressourcerne.

Hvad er autorisation?

Godkendelse sker efter godkendelse. Enhver autorisation skal have en godkendelsesproces. Det er processen med at give brugere adgang til ressourcer fra systemerne eller et websted. I denne vejledning autoriserer vi loggede brugere til at få adgang til brugerens data. Hvis brugeren ikke er logget ind, vil de ikke kunne bruge adgang til dataene.

De bedste eksempler på autorisation er sociale medieplatforme som Facebook og Twitter. Du kan ikke få adgang til indhold på sociale medier uden at have en konto.

Et andet eksempel på autorisation er abonnementsbaseret indhold, din autentificering kan ske ved at logge ind på hjemmesiden, men du vil ikke få adgang til indholdet, før du ikke har abonneret.

Forudsætning

Inden du går videre, går jeg ud fra, at du har en grundlæggende forståelse af Javascript og MongoDB og et godt kendskab til Nodejs.

Sørg for, at du har installeret node og npm på din lokale maskine. For at kontrollere, om node og npm er installeret på din computer, skal du åbne kommandoprompten og skrive node -v og npm -v. Dette skulle vise følgende resultat.

Dine versioner kan afvige fra mine. NPM bliver automatisk downloadet med noden. Hvis du ikke har downloadet det endnu, skal du downloade det fra NodeJS hjemmeside.

Du skal bruge en IDE (Integrated Development Environment) for at skrive kode. I denne tutorial bruger jeg VS-kodeeditor. Hvis du har en anden, kan du også bruge den. Hvis du ikke har nogen IDE installeret på din computer, kan du downloade den fra Visual Studio hjemmeside. Download det baseret på dit lokale system.

  Sådan får du en funktionsrig billedfremviser i Chrome

Projektopsætning

Opret et mappenavn nodeapi hvor som helst på din lokale computer, og åbn den derefter med vs-code. Åbn vs-code-terminalen, og initialiser derefter node-pakkehåndteringen ved at skrive.

npm init -y

Sørg for, at du er på nodeapi-mappen.

Ovenstående kommando vil oprette en package.json-fil, der indeholder alle de afhængigheder, som vi skal bruge i dette projekt.

Nu vil vi downloade alle de ovennævnte pakker, skriv nu og indtast dem i terminalen.

npm install express dotenv jsonwebtoken mongoose bcryptjs

Nu vil du have filer og mapper, som vist nedenfor.

Oprettelse af server og tilslutning af database

Opret nu en fil med navnet index.js og en mappe med navnet config. Inde i config skal du oprette to filer med navnet conn.js for at oprette forbindelse til databasen og config.env for at erklære miljøvariabler. Skriv den givne kode nedenfor i de 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}`);
})

Hvis du bruger dotenv, så konfigurer det i din index.js-fil, før du kalder andre filer, der bruger 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

Jeg bruger mongo-DB Atlas URI, du kan også bruge localhost.

Oprettelse af modeller og ruter

Model er et layout af dine data i Mongo-DB-databasen og vil blive gemt som et JSON-dokument. For at skabe en model skal vi bruge mongoose-skemaet.

Routing refererer til, hvordan en applikation reagerer på klientanmodninger. Vi vil bruge ekspres-router-funktionen til at oprette ruter.

Routingmetoder tager normalt to argumenter. Den første er rute, og den anden er tilbagekaldsfunktionen til at definere, hvad denne rute ville gøre på klientens anmodning.

Det tager også et tredje argument som en middleware-funktion, når det er nødvendigt, som i godkendelsesprocessen. Da vi bygger autentificeret API, vil vi også bruge middleware-funktionen til at autorisere og autentificere brugere.

Nu vil vi oprette to mapper med navnet ruter og modeller. Inde i ruter skal du oprette et filnavn userRoute.js og inde i modelmappen skal du oprette et filnavn userModel.js. Efter oprettelse af filer skal du skrive følgende kode i de 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)=>{

})

Implementering af rutefunktionalitet og oprettelse af JWT-tokens

Hvad er JWT?

JSON web-tokens (JWT) er et javascript-bibliotek, der opretter og verificerer tokens. Det er en åben standard, der bruges til at dele information mellem to parter – en klient og en server. Vi vil bruge to funktioner i JWT. Den første funktion er tegn for at oprette et nyt token, og den anden funktion er verificere for at bekræfte tokenet.

  Hvordan man fjernåbner en GUI-applikation med PuTTY

Hvad er bcryptjs?

Bcryptjs er en hashing-funktion skabt af Niels Provos og David Mazières. Den bruger en hash-algoritme til at hash-kode adgangskoden. Den har to mest almindelige funktioner, som vi vil bruge i dette projekt. Den første bcryptjs-funktion er hash til at generere hashværdi, og den anden funktion er sammenligningsfunktion til at sammenligne adgangskoder.

Implementer rutefunktionalitet

Tilbagekaldsfunktionen i routing tager tre argumenter, anmodning, svar og næste funktion. Det næste argument er valgfrit; videregiv kun dette, når du har brug for dette. Disse argumenter skal være i anmodningen, svaret og næste rækkefølge. Rediger nu filerne userRoute.js, config.env og index.js med følgende 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;

Hvis du bruger Async-funktionen, skal du bruge try-catch-blok, ellers vil det give en uhåndteret løfteafvisningsfejl.

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

Oprettelse af middleware for at godkende bruger

Hvad er middleware?

Middleware er en funktion, der har adgang til anmodningen, svarobjektet og næste funktion i anmodning-svar-cyklussen. Den næste funktion påkaldes, når funktionsudførelsen er fuldført. Som jeg nævnte ovenfor, brug next() når du skal udføre en anden tilbagekaldsfunktion eller middleware-funktion.

  Sådan oversætter du undertekster på Netflix [Chrome]

Opret nu en mappe med navnet middleware, og inde i den, opret filnavnet som auth.js og skriv følgende kode.

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;

Installer nu cookie-parser-biblioteket for at konfigurere cookieParser i din app. cookieParser hjælper dig med at få adgang til det token, der er gemt i cookien. Hvis du ikke har cookieParser konfigureret i din nodejs-app, vil du ikke kunne få adgang til cookies fra overskrifterne på anmodningsobjektet. Skriv nu i terminalen for at downloade cookie-parser.

npm i cookie-parser

Nu har du en cookieParser installeret. Konfigurer din app ved at ændre index.js-filen og tilføje middleware til “/user/”-ruten.

index.js fil

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;

Ruten “/bruger” er kun tilgængelig, når brugeren er logget ind.

Kontrol af API’erne på POSTMAN

Før du tjekker API’er, skal du ændre filen package.json. Tilføj følgende kodelinjer.

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

Du kan starte serveren ved at skrive npm start, men den kører kun én gang. For at holde din server kørende, mens du ændrer filer, skal du bruge nodemon. Download det ved at skrive i terminalen

npm install -g nodemon

-g flag vil downloade nodemonen globalt på dit lokale system. Du behøver ikke at downloade det igen og igen for hvert nyt projekt.

For at køre serveren skal du skrive npm run dev i terminalen. Du får følgende resultat.

Til sidst er din kode udfyldt, og serveren kører korrekt, gå til postmand og tjek om den virker.

Hvad er POSTMAN?

POSTMAN er et softwareværktøj til at designe, bygge, udvikle og teste API.

Hvis du ikke har downloadet postbudet på din computer, skal du downloade det fra postbuds hjemmeside.

Åbn nu postbudet og opret et samlingsnavn nodeAPItest, og inde i det, opret tre anmodninger: register, login og bruger. Du skal have følgende filer.

Når du sender JSON-data til “localhost:5000/api/register”, får du følgende resultat.

Da vi også opretter og gemmer tokens i cookies under registreringen, kan du få brugeroplysningerne, når du anmoder om ruten “localhost:5000/api/user”. Du kan tjekke resten af ​​anmodningerne på POSTMAN.

Hvis du vil have den komplette kode, kan du få den fra min github konto.

Konklusion

I denne vejledning har vi lært, hvordan man anvender godkendelse til NodeJS API ved hjælp af JWT-tokens. Vi gav også brugere tilladelse til at få adgang til brugerdataene.

GLÆDELIG KODNING!