Rumah >hujung hadapan web >tutorial js >Pengesahan selamat dalam nodejs dengan pangkalan data mongodb.

Pengesahan selamat dalam nodejs dengan pangkalan data mongodb.

DDD
DDDasal
2024-11-05 11:19:02619semak imbas

A secure authentication in nodejs with mongodb database.

Panduan Rujukan Pakej Node.js

Kebergantungan Teras

Rangka Kerja & Pelayan Web

  • ekspres (^4.18.2)
    • Rangka kerja aplikasi web
    • Mengendalikan penghalaan, perisian tengah dan permintaan/tindak balas HTTP
    • Asas teras untuk membina API dan aplikasi web

Pangkalan data

  • mongoose (^7.0.0)
    • Alat pemodelan objek MongoDB
    • Menyediakan penyelesaian berasaskan skema untuk memodelkan data aplikasi
    • Mengendalikan operasi pangkalan data dan perhubungan

Pakej Keselamatan

  • jsonwebtoken (^9.0.0)

    • Mencipta dan mengesahkan Token Web JSON (JWT)
    • Digunakan untuk pengesahan pengguna dan pertukaran maklumat selamat
  • bcryptjs (^2.4.3)

    • Cincang dan bandingkan kata laluan dengan selamat
    • Melindungi kata laluan pengguna dalam pangkalan data
  • topi keledar (^6.0.1)

    • Menambahkan pengepala keselamatan pada respons HTTP
    • Melindungi daripada kelemahan web biasa
    • Menetapkan pelbagai pengepala HTTP untuk keselamatan
  • kor (^2.8.5)

    • Mendayakan Perkongsian Sumber Rentas Asal
    • Mengawal domain yang boleh mengakses API anda
    • Penting untuk aplikasi web dengan bahagian hadapan/belakang yang berasingan

Pengesahan & Konfigurasi

  • joi (^17.9.0)

    • Pustaka pengesahan data
    • Mengesahkan badan permintaan, parameter pertanyaan dan input lain
    • Memastikan integriti dan format data
  • dotenv (^16.0.3)

    • Memuatkan pembolehubah persekitaran daripada fail .env
    • Mengurus tetapan konfigurasi
    • Memastikan data sensitif selamat

Ketergantungan Pembangunan

Alat Pembangunan

  • nodemon (^2.0.22)
    • Memantau perubahan fail semasa pembangunan
    • Memulakan semula pelayan secara automatik
    • Meningkatkan aliran kerja pembangunan

Contoh Penggunaan

// Express server setup
const express = require('express');
const app = express();

// Security middleware
const helmet = require('helmet');
const cors = require('cors');
app.use(helmet());
app.use(cors());

// Environment variables
require('dotenv').config();

// Database connection
const mongoose = require('mongoose');
mongoose.connect(process.env.MONGODB_URI);

// Validation example
const Joi = require('joi');
const schema = Joi.object({
    email: Joi.string().email().required()
});

// Password hashing
const bcrypt = require('bcryptjs');
const hashedPassword = await bcrypt.hash('password', 10);

// JWT authentication
const jwt = require('jsonwebtoken');
const token = jwt.sign({userId: 123}, process.env.JWT_SECRET);

Lengkapkan Panduan Struktur Projek Node.js dan Mongoose

Struktur Projek

project-root/
├── src/
│   ├── config/
│   │   ├── database.js
│   │   └── config.js
│   ├── models/
│   │   ├── user.model.js
│   │   └── product.model.js
│   ├── controllers/
│   │   ├── user.controller.js
│   │   └── product.controller.js
│   ├── routes/
│   │   ├── user.routes.js
│   │   └── product.routes.js
│   ├── middleware/
│   │   ├── auth.middleware.js
│   │   └── error.middleware.js
│   ├── utils/
│   │   ├── logger.js
│   │   └── validators.js
│   └── app.js
├── .env
├── .gitignore
└── package.json

1. Persediaan Awal

package.json

{
  "name": "node-mongoose-project",
  "version": "1.0.0",
  "main": "src/app.js",
  "scripts": {
    "start": "node src/app.js",
    "dev": "nodemon src/app.js"
  },
  "dependencies": {
    "express": "^4.18.2",
    "mongoose": "^7.0.0",
    "dotenv": "^16.0.3",
    "joi": "^17.9.0",
    "jsonwebtoken": "^9.0.0",
    "bcryptjs": "^2.4.3",
    "cors": "^2.8.5",
    "helmet": "^6.0.1"
  },
  "devDependencies": {
    "nodemon": "^2.0.22"
  }
}

.env

PORT=3000
MONGODB_URI=mongodb://localhost:27017/your-database
JWT_SECRET=your-secret-key
NODE_ENV=development

.giignore

node_modules/
.env
logs/
*.log

2. Persediaan Konfigurasi

src/config/config.js

require('dotenv').config();

module.exports = {
    port: process.env.PORT || 3000,
    mongoUri: process.env.MONGODB_URI,
    jwtSecret: process.env.JWT_SECRET,
    nodeEnv: process.env.NODE_ENV || 'development',
    jwtExpiresIn: '1d'
};

src/config/database.js

const mongoose = require('mongoose');
const config = require('./config');
const logger = require('../utils/logger');

const connectDB = async () => {
    try {
        await mongoose.connect(config.mongoUri, {
            useNewUrlParser: true,
            useUnifiedTopology: true
        });
        logger.info('MongoDB connected successfully');
    } catch (error) {
        logger.error('MongoDB connection error:', error);
        process.exit(1);
    }
};

module.exports = connectDB;

3. Definisi Model

src/models/user.model.js

const mongoose = require('mongoose');
const bcrypt = require('bcryptjs');
const jwt = require('jsonwebtoken');
const config = require('../config/config');

const userSchema = new mongoose.Schema({
    name: {
        type: String,
        required: [true, 'Name is required'],
        trim: true,
        minlength: 3,
        maxlength: 50
    },
    email: {
        type: String,
        required: [true, 'Email is required'],
        unique: true,
        lowercase: true,
        trim: true
    },
    password: {
        type: String,
        required: [true, 'Password is required'],
        minlength: 6,
        select: false
    },
    role: {
        type: String,
        enum: ['user', 'admin'],
        default: 'user'
    }
}, {
    timestamps: true
});

// Pre-save middleware to hash password
userSchema.pre('save', async function(next) {
    if (!this.isModified('password')) return next();
    this.password = await bcrypt.hash(this.password, 12);
    next();
});

// Instance methods
userSchema.methods.generateAuthToken = function() {
    return jwt.sign(
        { id: this._id, role: this.role },
        config.jwtSecret,
        { expiresIn: config.jwtExpiresIn }
    );
};

userSchema.methods.comparePassword = async function(candidatePassword) {
    return await bcrypt.compare(candidatePassword, this.password);
};

const User = mongoose.model('User', userSchema);
module.exports = User;

src/models/product.model.js

const mongoose = require('mongoose');

const productSchema = new mongoose.Schema({
    name: {
        type: String,
        required: true,
        trim: true
    },
    price: {
        type: Number,
        required: true,
        min: 0
    },
    description: String,
    category: {
        type: String,
        required: true
    },
    createdBy: {
        type: mongoose.Schema.Types.ObjectId,
        ref: 'User',
        required: true
    }
}, {
    timestamps: true
});

const Product = mongoose.model('Product', productSchema);
module.exports = Product;

4. Pengawal

src/controllers/user.controller.js

const User = require('../models/user.model');
const { validateUser } = require('../utils/validators');
const logger = require('../utils/logger');

exports.register = async (req, res) => {
    try {
        const { error } = validateUser(req.body);
        if (error) return res.status(400).json({ error: error.details[0].message });

        const user = await User.create(req.body);
        const token = user.generateAuthToken();

        res.status(201).json({
            status: 'success',
            token,
            data: { user }
        });
    } catch (error) {
        logger.error('Registration error:', error);
        res.status(400).json({
            status: 'fail',
            message: error.message
        });
    }
};

exports.login = async (req, res) => {
    try {
        const { email, password } = req.body;

        const user = await User.findOne({ email }).select('+password');
        if (!user || !(await user.comparePassword(password))) {
            return res.status(401).json({
                status: 'fail',
                message: 'Invalid email or password'
            });
        }

        const token = user.generateAuthToken();
        res.json({
            status: 'success',
            token
        });
    } catch (error) {
        logger.error('Login error:', error);
        res.status(400).json({
            status: 'fail',
            message: error.message
        });
    }
};

5. Laluan

src/routes/user.routes.js

const express = require('express');
const router = express.Router();
const userController = require('../controllers/user.controller');
const auth = require('../middleware/auth.middleware');

router.post('/register', userController.register);
router.post('/login', userController.login);
router.get('/profile', auth, userController.getProfile);

module.exports = router;

6. Middleware

src/middleware/auth.middleware.js

const jwt = require('jsonwebtoken');
const config = require('../config/config');
const User = require('../models/user.model');

module.exports = async (req, res, next) => {
    try {
        const token = req.headers.authorization?.replace('Bearer ', '');

        if (!token) {
            return res.status(401).json({
                status: 'fail',
                message: 'No token provided'
            });
        }

        const decoded = jwt.verify(token, config.jwtSecret);
        const user = await User.findById(decoded.id);

        if (!user) {
            return res.status(401).json({
                status: 'fail',
                message: 'User not found'
            });
        }

        req.user = user;
        next();
    } catch (error) {
        res.status(401).json({
            status: 'fail',
            message: 'Invalid token'
        });
    }
};

src/middleware/error.middleware.js

const logger = require('../utils/logger');

module.exports = (err, req, res, next) => {
    logger.error(err.stack);

    if (err.name === 'ValidationError') {
        return res.status(400).json({
            status: 'fail',
            message: err.message
        });
    }

    if (err.code === 11000) {
        return res.status(400).json({
            status: 'fail',
            message: 'Duplicate field value'
        });
    }

    res.status(err.status || 500).json({
        status: 'error',
        message: err.message || 'Internal server error'
    });
};

7. Utiliti

src/utils/logger.js

const winston = require('winston');
const config = require('../config/config');

const logger = winston.createLogger({
    level: config.nodeEnv === 'development' ? 'debug' : 'info',
    format: winston.format.combine(
        winston.format.timestamp(),
        winston.format.json()
    ),
    transports: [
        new winston.transports.File({ filename: 'logs/error.log', level: 'error' }),
        new winston.transports.File({ filename: 'logs/combined.log' })
    ]
});

if (config.nodeEnv === 'development') {
    logger.add(new winston.transports.Console({
        format: winston.format.simple()
    }));
}

module.exports = logger;

src/utils/validators.js

const Joi = require('joi');

exports.validateUser = (user) => {
    const schema = Joi.object({
        name: Joi.string().min(3).max(50).required(),
        email: Joi.string().email().required(),
        password: Joi.string().min(6).required(),
        role: Joi.string().valid('user', 'admin')
    });

    return schema.validate(user);
};

exports.validateProduct = (product) => {
    const schema = Joi.object({
        name: Joi.string().required(),
        price: Joi.number().min(0).required(),
        description: Joi.string(),
        category: Joi.string().required()
    });

    return schema.validate(product);
};

8. Fail Permohonan Utama

src/app.js

const express = require('express');
const cors = require('cors');
const helmet = require('helmet');
const config = require('./config/config');
const connectDB = require('./config/database');
const errorMiddleware = require('./middleware/error.middleware');
const userRoutes = require('./routes/user.routes');
const productRoutes = require('./routes/product.routes');
const logger = require('./utils/logger');

// Initialize express app
const app = express();

// Connect to MongoDB
connectDB();

// Middleware
app.use(helmet());
app.use(cors());
app.use(express.json());
app.use(express.urlencoded({ extended: true }));

// Routes
app.use('/api/users', userRoutes);
app.use('/api/products', productRoutes);

// Error handling
app.use(errorMiddleware);

// Start server
app.listen(config.port, () => {
    logger.info(`Server running in ${config.nodeEnv} mode on port ${config.port}`);
});

// Handle unhandled promise rejections
process.on('unhandledRejection', (err) => {
    logger.error('UNHANDLED REJECTION! Shutting down...');
    logger.error(err.name, err.message);
    process.exit(1);
});

Menjalankan Aplikasi

  1. Pasang kebergantungan:
npm install
  1. Sediakan pembolehubah persekitaran anda dalam .env

  2. Mulakan pelayan pembangunan:

npm run dev
  1. Untuk pengeluaran:
npm start

Menguji API

Uji titik akhir API menggunakan alatan seperti Posman atau curl:

# Register a new user
curl -X POST http://localhost:3000/api/users/register \
  -H "Content-Type: application/json" \
  -d '{"name": "John Doe", "email": "john@example.com", "password": "password123"}'

# Login
curl -X POST http://localhost:3000/api/users/login \
  -H "Content-Type: application/json" \
  -d '{"email": "john@example.com", "password": "password123"}'

Panduan berstruktur ini menyediakan persediaan lengkap untuk aplikasi Node.js dan Mongoose dengan ciri organisasi, pengendalian ralat dan keselamatan yang betul. Setiap fail mempunyai tanggungjawab khusus, menjadikan pangkalan kod boleh diselenggara dan berskala.

Atas ialah kandungan terperinci Pengesahan selamat dalam nodejs dengan pangkalan data mongodb.. Untuk maklumat lanjut, sila ikut artikel berkaitan lain di laman web China PHP!

Kenyataan:
Kandungan artikel ini disumbangkan secara sukarela oleh netizen, dan hak cipta adalah milik pengarang asal. Laman web ini tidak memikul tanggungjawab undang-undang yang sepadan. Jika anda menemui sebarang kandungan yang disyaki plagiarisme atau pelanggaran, sila hubungi admin@php.cn
Artikel sebelumnya:TCP vs protokol UDPArtikel seterusnya:TCP vs protokol UDP