[[:oktatas:web:back-end_framework:express|< Express]]
====== Express - Azonosítás ======
* **Szerző:** Sallai András
* Copyright (c) Sallai András, 2023
* Szerkesztve: 2024
* Licenc: [[https://creativecommons.org/licenses/by-sa/4.0/|CC Attribution-Share Alike 4.0 International]]
* Web: https://szit.hu
===== Bevezetés =====
Az útvonalak védelmét úgy tudjuk ellátni, ha van felhasználó aki be tud jelentkezni, és sikeres bejelentkezés után kap egy tokent, amivel majd elérheti a védett útvonalakat.
Az útválasztóban beállítható, hogy adott útvonalak csak akkor érhetők el, ha visszakapjuk az érvényes tokent. Ehhez egy köztes szoftvert fogunk használni a tokenek ellenőrzésére. A tokeneket sikeres bejelentkezéskor készítjük.
A példánkban a jsonwebtoken-t fogjuk használni. A token szerveroldalon keletkezik, de kliens oldalon tároljuk.
===== Felhasználó felvétele =====
==== User modell ====
A felhasználók kezeléséhez egy User nevű modellben leírjuk a felhasználóról mit tárolunk.
const { DataTypes } = require('sequelize')
const sequelize = require('../database/mariadb')
const User = sequelize.define('User', {
id: {
type: DataTypes.INTEGER,
autoIncrement: true,
primaryKey: true
},
name: { type: DataTypes.STRING, allowNull: false },
email: { type: DataTypes.STRING, allowNull: true },
password: { type: DataTypes.STRING , allowNull: false }
})
//A model és az adatbázis szinkronizálása, nem erőltetve.
sequelize.sync({
force: false
})
module.exports = User
==== AuthController ====
Az Auth kontroller helyett készíthetnék akár egy User kontrollert is, de az Auth névhez jobban illek majd a login() metódus.
Telepítsük a bcryptjs csomagot:
npm install bcryptjs
A bcryptjs a bcrypt JavaScriptre optimalizált változata.
Készítsük el az app/controllers/authcontroller.js fájlt:
const bcrypt = require('bcryptjs')
const User = require('../models/user')
const AuthController = {
async register(req, res) {
var clientError = false;
try {
if(!req.body.name ||
!req.body.email ||
!req.body.password ||
!req.body.password_confirmation) {
clientError = true
throw new Error('Error! Bad request data!')
}
if(req.body.password != req.body.password_confirmation) {
clientError = true
throw new Error('Error! The two password is not same!')
}
const user = await User.findOne({
where: { name: req.body.name }
})
if(user) {
clientError = true
throw new Error('Error! User already exists: ' + user.name)
}
AuthController.tryRegister(req, res)
} catch (error) {
if (clientError) {
res.status(400)
}else {
res.status(500)
}
await res.json({
success: false,
message: 'Error! User creation failed!',
error: error.message
})
}
},
async tryRegister(req, res) {
const user = {
name: req.body.name,
email: req.body.email,
password: bcrypt.hashSync(req.body.password)
}
await User.create(user)
.then( result => {
res.status(201)
res.json({
succes: true,
data: result
})
})
}
}
module.exports = AuthController
==== Routing ====
Írjunk egy új útválasztó sort:
const AuthController = require('../controllers/authcontroller')
//...
router.post('/register', AuthController.register)
A teljes kód:
const Router = require('express');
const router = Router();
const EmployeeController = require('../controllers/employeecontroller')
const AuthController = require('../controllers/authcontroller')
router.get('/employees', EmployeeController.index)
router.post('/employees', EmployeeController.store)
router.put('/employees/:id', EmployeeController.update)
router.delete('/employees/:id', EmployeeController.destroy)
router.post('/register', AuthController.register)
module.exports = router
===== Bejelentkezés =====
==== APP_KEY ====
Vegyük fel a .env fájlban az APP_KEY tulajdonságot:
APP_KEY=3434384383343
Írjunk bele, legalább 32 számot és betűt.
==== Az authcontroller.js-ben ====
const jwt = require('jsonwebtoken')
//...
async login(req, res) {
try {
if(!req.body.name || !req.body.password) {
res.status(400)
throw new Error('Error! Bad name or password!')
}
const user = await User.findOne({
where: { name: req.body.name }
})
if(!user) {
res.status(404)
throw new Error('Error! User not found!')
}
var passwordIsValid = await bcrypt.compare(
req.body.password,
user.dataValues.password
);
if(!passwordIsValid) {
res.status(401)
throw new Error('Error! Password is not valid!')
}
AuthController.tryLogin(req, res, user)
} catch (error) {
res.json({
success: false,
message: 'Error! The login is failed!',
error: error.message
})
}
},
async tryLogin(req, res, user) {
var token = jwt.sign({ id: user.id }, config.app.key, {
expiresIn: 86400 //24 óra
})
res.status(200).json({
id: user.id,
name: user.name,
email: user.email,
accessToken: token
})
}
}
==== Útválasztás ====
router.post('/login', AuthController.login)
===== Útvonal védelme =====
==== Token ellenőrző köztes szoftver ====
const jwt = require("jsonwebtoken");
require('dotenv').config()
exports.verifyToken = (req, res, next) => {
let authData = req.headers.authorization;
if(!authData) {
return res.status(403).send({
message: 'No token provided!'
})
}
let token = authData.split(' ')[1];
jwt.verify(token, process.env.APP_KEY, (err, decoded) => {
if(err) {
return res.status(401).send({
message: "Unauthorized!"
})
}
req.userId = decoded.id;
next()
})
};
==== Útválasztás ====
const { verifyToken } = require('../middleware/authjwt');
//...
router.post('/employees', [verifyToken], EmployeeController.store)
Ellenőrzés, például:
http post localhost:8000/api/employees
name='Verdi Ernő' city='Szeged'
-A bearer -a eyJhbG
A -a után a token írjuk, ami valójában jóval hosszabb.
Állítsuk be az update és delete műveletre is:
router.delete('/employees/:id', [verifyToken], employees.destroy);
router.put('/employees/:id', [verifyToken], employees.update);