برنامه نویسی

سیستم احراز هویت Socket.IO با JWT

منابع کمی برای بررسی در مورد احراز هویت وجود دارد socket.io اتصالات من اخیراً یک پروژه برنامه نویسی را به پایان رساندم که شامل احراز هویت اتصالات سوکت بود. در طول پروژه، تمام تلاشم را کردم تا منابعی را جستجو کنم که نحوه ایجاد یک سیستم احراز هویت را توضیح دهد socket.io اتصالات، اما فایده ای نداشت. آنها یا برای مورد استفاده پروژه من مفید نبودند یا نامشخص بودند. با کمی تلاش، من توانستم یک سیستم احراز هویت امن برای سرور بسازم تا هر کلاینت متصل قبل از برقراری ارتباط احراز هویت شود.

در این مقاله، من شما را با فرآیند گام به گام ساختن یک امن آشنا می کنم socket.io سیستم احراز هویت با توکن های وب JSON (JWT). این دانش همچنین می تواند به کتابخانه های احراز هویت دیگری مانند passport.js یا کتابخانه های احراز هویت زبان های برنامه نویسی دیگر، زیرا من از NodeJs استفاده خواهم کرد.

بیایید شیرجه بزنیم!

بررسی اجمالی

من تمام جزئیات کدهای نمونه را توضیح خواهم داد تا همه بدون توجه به پشته های فنی که با آنها آشنا هستند، آنها را دنبال کنند.

با این حال، ما سیستم احراز هویت را با استفاده از MongoDB خواهیم ساخت User مجموعه، یک http سرور، یک اکسپرس app، و socket.io نمونه سرور علاوه بر این، ما یک نمونه کد سمت سرویس گیرنده را برای نشان دادن موارد استفاده آن ارائه خواهیم کرد.

را User مجموعه برای ذخیره و بازیابی اطلاعات کاربری کاربران استفاده می شود.

را http سرور برای گوش دادن به HTTP و درخواست های اتصال سوکت استفاده می شود.

اکسپرس app برای راه‌اندازی کنترل‌کننده‌های تابع که نقاط پایانی احراز هویت ثبت نام و ورود به سیستم را مدیریت می‌کنند، استفاده می‌شود. پاسخی حاوی یک رمز وب JSON (JWT) در احراز هویت موفقیت آمیز انتظار می رود.

را socket.io نمونه سرور مسئول مدیریت رویدادهای اتصال سوکت است. یک تابع میان‌افزار برای اعتبارسنجی JWT ارسال شده توسط مشتری استفاده می‌شود و اطمینان حاصل می‌کند که فقط کاربران احراز هویت شده می‌توانند درخواست اتصال سوکت وب را انجام دهند.

کد سمت سرویس گیرنده برای ایجاد درخواست های HTTP به نقاط پایانی احراز هویت استفاده می شود و پاسخ JWT را ذخیره می کند، که سپس برای ایجاد درخواست های اتصال سوکت وب به socket.io نمونه سرور

سیستم احراز هویت

در این بخش سیستم احراز هویت را می سازیم. از مدل پایگاه داده تا socket.io راه اندازی میان افزار احراز هویت نمونه سرور.

مدل پایگاه داده

بیایید با ایجاد آن شروع کنیم User طرح واره مدل

const mongoose = require('mongoose');

const userSchema = new mongoose.Schema({
    username: String,
    email: String,
    password: String,
  });

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

module.exports = User;

وارد حالت تمام صفحه شوید

از حالت تمام صفحه خارج شوید

ما وارد می کنیم mongoose ماژول، که قابلیت تعریف و تعامل با طرحواره ها و مدل های MongoDB را فراهم می کند.

آ userSchema با استفاده از ایجاد می شود mongoose.Schema سازنده، ساختار و انواع داده های شی کاربر را مشخص می کند. این طرح شامل فیلدهایی برای username، email، و password، هر کدام از نوع String.

را userSchema سپس برای ایجاد a استفاده می شود User مدل. این مدل امکان تعامل با مجموعه “کاربر” در پایگاه داده MongoDB متصل را فراهم می کند.

در نهایت، User مدل صادر می شود تا برای استفاده در سیستم احراز هویت در دسترس باشد.

کنترل کننده های احراز هویت اکسپرس

در اینجا، ما توابع کنترل کننده را برای نقاط پایانی ثبت نام و ورود به سیستم با استفاده از Express ایجاد خواهیم کرد.

اول از همه، اجازه دهید ماژول های لازم را وارد کنیم.

const express = require('express');
const bcrypt = require('bcrypt');
const jwt = require('jsonwebtoken');
const User = require("models/user.model");

وارد حالت تمام صفحه شوید

از حالت تمام صفحه خارج شوید

در کد بالا، express، bcrypt، و jsonwebtoken همه ماژول ها به متغیرهای مربوطه خود اختصاص داده می شوند. علاوه بر این، User مدل پایگاه داده برای فعال کردن ذخیره سازی و تأیید اطلاعات کاربر وارد شده است.

سپس Express را ایجاد می کنیم app.

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

// Register API endpoint
app.post('/auth/register', registerUser);

// Login API endpoint
app.post('/auth/login', loginUser);

وارد حالت تمام صفحه شوید

از حالت تمام صفحه خارج شوید

ما با فراخوانی شروع می کنیم express() تابع ارائه شده توسط ماژول اکسپرس، که به ما امکان می دهد نقاط پایانی را به همراه توابع کنترل کننده مربوطه آنها ثبت کنیم. علاوه بر این، ما را متصل می کنیم express.json() میان افزار برای تجزیه بارهای درخواستی JSON. دو POST متعاقباً متدها به Express اضافه می شوند app، آن را قادر می سازد تا به درخواست های مشتری در /auth/register و /auth/login نقاط پایانی این درخواست ها توسط registerUser و loginUser توابع کنترل کننده، به ترتیب.

بعد، ما به توسعه منطق برای ادامه می‌دهیم registerUser عملکرد کنترل کننده

// User Registration Handler Function
async function registerUser(req, res) {
  try {
    const { username, email, password } = req.body;

    // Check if the username or email already exists
    const existingUser = await User.findOne().or([{ username }, { email }]);
    if (existingUser) {
      return res.status(400).json({ message: 'Username or email already exists' });
    }

    // Hash the password
    const hashedPassword = await bcrypt.hash(password, 10);

    // Create a new user
    const newUser = new User({
      username,
      email,
      password: hashedPassword,
    });

    // Save the user to the database
    await newUser.save();
    res.status(201).json({ message: 'Registration successful' });
  } catch (error) {
    console.error('Registration error', error);
    res.status(500).json({ message: 'Registration error' });
  }
}

وارد حالت تمام صفحه شوید

از حالت تمام صفحه خارج شوید

ما با تخریب ساختار شروع می کنیم username، email، و password از req.body شی، که حاوی بدنه درخواست ارسال شده توسط مشتری است. در مرحله بعد، ما یک بررسی برای منحصر به فرد بودن آن انجام می دهیم email و username با جستجوی آنها در پایگاه داده اگر یکی از آنها پیدا شد، الف 400 پاسخ بد به درخواست برگشت داده می شود. در غیر این صورت، تابع با استفاده از رمز عبور را هش می کند bcrypt.hash() عملکرد ارائه شده توسط ماژول bcrypt. رمز عبور هش شده به همراه گذرواژه تخریب شده username و email خواص، سپس برای ماندگاری در پایگاه داده ذخیره می شوند. در صورت عدم وجود خطا، الف 201 پاسخ ایجاد شده برگردانده می شود. با این حال، اگر در حین اجرای تابع کنترل کننده خطایی رخ دهد، الف 500 پاسخ خطای سرور داخلی برگردانده می شود.

بعد، ما به توسعه منطق برای ادامه می دهیم loginUser عملکرد کنترل کننده

// ...

// User Login Handler Function
async function loginUser(req, res) {
  try {
    const { username, password } = req.body;

    // Check if the username exists
    const user = await User.findOne({ username });
    if (!user) {
      return res.status(400).json({ message: 'Invalid username or password' });
    }

    // Compare the password
    const isPasswordValid = await bcrypt.compare(password, user.password);
    if (!isPasswordValid) {
      return res.status(400).json({ message: 'Invalid username or password' });
    }

    // Generate a JWT
    const token = jwt.sign({ userId: user._id }, process.env.SECRET_KEY);

    res.json({ token, message: 'Login successful' });
  } catch (error) {
    console.error('Login error', error);
    res.status(500).json({ message: 'Login error' });
  }
}

// ...

وارد حالت تمام صفحه شوید

از حالت تمام صفحه خارج شوید

ما ابتدا بررسی می کنیم که آیا تخریب شده است یا خیر username در پایگاه داده وجود دارد. اگر وجود نداشته باشد، الف 400 پاسخ بد به درخواست برگشت داده می شود. همچنین، اگر تخریب شده است password با رمز عبور ذخیره شده توسط کاربر در پایگاه داده مطابقت ندارد، a 400 پاسخ بد برگردانده می شود. با این حال، اگر گذرواژه‌ها مطابقت داشته باشند، با استفاده از کد JWT اقدام به تولید می‌کنیم jsonwebtoken مدول. این نشانه با تنظیم ایجاد می شود userId کلید ارزش _id ویژگی کاربر، که به طور خودکار توسط MongoDB به هر اطلاعات کاربر ذخیره شده اختصاص داده می شود. با فرض عدم رخ دادن خطایی، یک پاسخ حاوی توکن تولید شده برگردانده می شود.

بعد، Express را صادر می کنیم app.

// ...
module.exports = app;

وارد حالت تمام صفحه شوید

از حالت تمام صفحه خارج شوید

کد کامل برای نقاط پایانی احراز هویت را می توانید در زیر بیابید.

const express = require('express');
const bcrypt = require('bcrypt');
const jwt = require('jsonwebtoken');
const User = require("models/user.model");

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

// Register API endpoint
app.post('/auth/register', registerUser);

// Login API endpoint
app.post('/auth/login', loginUser);

// User Registration Handler Function
async function registerUser(req, res) {
  try {
    const { username, email, password } = req.body;

    // Check if the username or email already exists
    const existingUser = await User.findOne().or([{ username }, { email }]);
    if (existingUser) {
      return res.status(400).json({ message: 'Username or email already exists' });
    }

    // Hash the password
    const hashedPassword = await bcrypt.hash(password, 10);

    // Create a new user
    const newUser = new User({
      username,
      email,
      password: hashedPassword,
    });

    // Save the user to the database
    await newUser.save();
    res.json({ message: 'Registration successful' });
  } catch (error) {
    console.error('Registration error', error);
    res.status(500).json({ message: 'Registration error' });
  }
}

// User Login Handler Function
async function loginUser(req, res) {
  try {
    const { username, password } = req.body;

    // Check if the username exists
    const user = await User.findOne({ username });
    if (!user) {
      return res.status(400).json({ message: 'Invalid username or password' });
    }

    // Compare the password
    const isPasswordValid = await bcrypt.compare(password, user.password);
    if (!isPasswordValid) {
      return res.status(400).json({ message: 'Invalid username or password' });
    }

    // Generate a JWT
    const token = jwt.sign({ userId: user._id }, process.env.SECRET_KEY);

    res.json({ token, message: 'Login successful' });
  } catch (error) {
    console.error('Login error', error);
    res.status(500).json({ message: 'Login error' });
  }
}

module.exports = app;

وارد حالت تمام صفحه شوید

از حالت تمام صفحه خارج شوید

اکسپرس ما app اکنون آماده استفاده است.

راه اندازی سوکت سرور

حالا بیایید راه اندازی کنیم http و socket.io نمونه های سرور برای احراز هویت

const http = require('http');
const ioServer = require('socket.io');
const app = require('./app');

وارد حالت تمام صفحه شوید

از حالت تمام صفحه خارج شوید

ما با واردات شروع می کنیم http و socket.io ماژول ها، و همچنین Express ما app.

در مرحله بعد، به ایجاد نمونه هایی از the می پردازیم http و socket.io سرورها

// Create server from express app
const server = http.createServer(app);

// Create the socket server instance
const io = ioServer(server);

وارد حالت تمام صفحه شوید

از حالت تمام صفحه خارج شوید

ما استفاده می کنیم createServer() عملکرد ارائه شده توسط http ماژول برای ایجاد یک نمونه سرور HTTP. اکسپرس app سپس به تابع برای رسیدگی به درخواست‌ها به نقاط پایانی احراز هویت منتقل می‌شود. را http سرور سپس برای ایجاد یک استفاده می شود socket.io نمونه سرور

بعد، ما به اضافه کردن میان افزار احراز هویت ادامه می دهیم.

io.use(async (socket, next) => {
        try {
            const token = socket.handshake.auth.token;

            // Verify and decode the JWT
            const decoded = jwt.verify(token, process.env.SECRET_KEY);

            // Get the user information from the database
            const user = await User.findById(decoded.userId);
            if (!user) {
                throw new Error('User not found');
            }

            // Attach the user object to the socket
            socket.user = user;
            next();
        } catch (error) {
            console.error('Authentication error', error);
            next(new Error('Authentication error'));
        }
    });

    io.on('connection', (socket) => {
        // Handle Events after authentication
    }

وارد حالت تمام صفحه شوید

از حالت تمام صفحه خارج شوید

در کد بالا، زمانی که یک کلاینت به سرور متصل می شود، تابع میان افزار است io.use()ارائه شده توسط socket.io کتابخانه، فراخوانی می شود. در داخل تابع، ابتدا توکن JWT را از تابع بازیابی می کنیم socket.handshake.auth.token اموال ارسال شده توسط مشتری در هنگام دست دادن (اتصال). سپس با استفاده از یک کلید مخفی ذخیره شده در یک متغیر محیطی، رمز را تأیید و رمزگشایی می کند.

اگر توکن معتبر باشد، میان افزار اطلاعات کاربر را از پایگاه داده بر اساس شناسه کاربری استخراج شده از توکن بازیابی می کند. اگر کاربر پیدا شود، شی کاربر برای مراجعات بعدی به سوکت متصل می شود.

اگر در طی فرآیند احراز هویت خطایی رخ دهد، مانند رمز نامعتبر یا یافت نشدن کاربر، خطایی ایجاد می‌شود و میان‌افزار next() تابع با آرگومان خطا

پس از احراز هویت موفق، io.on('connection') کنترل کننده رویداد فعال می شود، که امکان مدیریت رویداد و ارتباط بیشتر با کاربر تأیید شده را فراهم می کند.

سیستم احراز هویت اکنون کامل است. اما برای گوش دادن به اتصالات، کد زیر را می توان به دلخواه اضافه یا سفارشی کرد.

server.listen(PORT, () => {
    console.log(` Server started running at ${PORT}`);
});

وارد حالت تمام صفحه شوید

از حالت تمام صفحه خارج شوید

جایی که PORT پورت ترجیحی برای گوش دادن به اتصالات است. همچنین فراموش نکنید که قبل از راه اندازی سرور به مجموعه MongoDB خود متصل شوید.

کد کامل برای راه اندازی سرور را می توانید در زیر مشاهده کنید.

const http = require('http');
const ioServer = require('socket.io');
const app = require('./app');
// Create server from express app
const server = http.createServer(app);

// Create the socket server instance
const io = ioServer(server);

io.use(async (socket, next) => {
        try {
            const token = socket.handshake.auth.token;

            // Verify and decode the JWT
            const decoded = jwt.verify(token, process.env.SECRET_KEY);

            // Get the user information from the database
            const user = await User.findById(decoded.userId);
            if (!user) {
                throw new Error('User not found');
            }

            // Attach the user object to the socket
            socket.user = user;
            next();
        } catch (error) {
            console.error('Authentication error', error);
            next(new Error('Authentication error'));
        }
    });

    io.on('connection', (socket) => {
        // Handle Events after authentication
    }

    server.listen(PORT, () => {
        console.log(` Server started running at ${PORT}`);
    });

وارد حالت تمام صفحه شوید

از حالت تمام صفحه خارج شوید

اتصال سمت مشتری

برای نشان دادن نحوه تنظیم سمت کلاینت برای استفاده از سیستم احراز هویت، از Axios برای ارسال درخواست به نقاط پایانی احراز هویت و استفاده از نشانه بازیابی شده برای اتصال به socket.io نمونه سرور

بیایید با ثبت نام کاربر شروع کنیم.

const axios = require('axios');
const io = require('socket.io-client');

const username="HayatsCodes";
const email="hayatscodes@gmail.com";
const password = 123456;
let token;

try {
    const response = await axios.post(`http://localhost:${PORT}/auth/register`, {
      username,
      email,
      password,
    });
    console.log(response.data.message); // Registration successful
  } catch (error) {
      console.error(error);
  }

وارد حالت تمام صفحه شوید

از حالت تمام صفحه خارج شوید

اولا، axios و socket.io-client کتابخانه ها وارد می شوند.

سپس با ایجاد یک کاربر در حال ثبت نام هستیم POST درخواست به /auth/register نقطه پایانی سرور URL سرور با استفاده از PORT متغیر، که باید شامل شماره پورت باشد.

مال کاربر username، email، و password به عنوان محموله درخواستی ارائه می شود. ما استفاده می کنیم await کلمه کلیدی برای ایجاد درخواست و ذخیره پاسخ در response متغیر.

اگر ثبت نام موفقیت آمیز باشد، پیام پاسخ به کنسول وارد می شود.

اگر در فرآیند ثبت نام خطایی رخ دهد، catch بلوک اجرا می شود و خطا با استفاده از آن در کنسول ثبت می شود console.error.

حالا بیایید وارد کاربر ثبت نام شده شویم.

try {
      const response = await axios.post(`http://localhost:${PORT}/auth/login`, {
        username,
        password,
      });
      token = response.data.token;
      console.log(response.data.message); // login successful
    } catch (error) {
        console.error(error.response.data); 
    }
};

وارد حالت تمام صفحه شوید

از حالت تمام صفحه خارج شوید

ما با ارسال یک درخواست POST به کاربر ثبت نام شده وارد می شویم /auth/login نقطه پایانی سرور در حالی که شامل username و password در محموله درخواستی

در صورت موفقیت آمیز بودن ورود، توکن از آن استخراج می شود response.data.token اموال، و message به کنسول وارد شده است.

سپس توکن به آن اختصاص داده می شود token متغیر برای استفاده در socket.io اتصال مشتری

اگر در فرآیند ثبت نام خطایی رخ دهد، catch بلوک اجرا می شود و خطا با استفاده از آن در کنسول ثبت می شود console.error.

حالا بیایید به socket.io سرور

const client = io(`http://localhost:${PORT}`, {
      auth: {
        token
      }
});
// handle events
client.on('connect', () => { console.log('connected!') });
// Additional event handling can follow

وارد حالت تمام صفحه شوید

از حالت تمام صفحه خارج شوید

در قطعه کد بالا، ما در حال ایجاد یک اتصال سمت کلاینت به آن هستیم socket.io سرور با استفاده از io عملکرد ارائه شده توسط socket.io-client کتابخانه

را io تابع با URL سرور سوکت و یک شی به عنوان آرگومان فراخوانی می شود که شامل عبارت است auth ویژگی. این ویژگی نشانه احراز هویت را مشخص می کند که در طی فرآیند دست دادن به سرور ارسال می شود. مقدار ذخیره شده قبلی token متغیر از درخواست ورود به سیستم به عنوان مقدار توکن ارائه می شود.

پس از برقراری ارتباط، client.on('connect') کنترل کننده رویداد برای گوش دادن به رویداد “connect” تنظیم شده است. هنگامی که کلاینت با موفقیت به سرور متصل می شود، عملکرد پاسخ به تماس اجرا می شود و “متصل است!” به کنسول

مدیریت رویداد و ارتباط اضافی با سرور را می توان در کنترل کننده رویداد مناسب اضافه کرد.

نتیجه

ساخت یک سیستم احراز هویت امن برای socket.io اتصالات به دلیل منابع محدود موجود می تواند یک کار چالش برانگیز باشد. با این حال، با استفاده از نشانه های وب JSON (JWT)، می توان یک سیستم احراز هویت قوی ایجاد کرد. در این مقاله به مراحل گام به گام ساخت چنین سیستمی از جمله راه اندازی مدل پایگاه داده، ایجاد نقاط پایانی احراز هویت با Express، پیاده سازی میان افزار احراز هویت با socket.ioو نشان دادن اتصالات سمت سرویس گیرنده با استفاده از axios و socket.io-client کتابخانه با دنبال کردن مثال ها و توضیحات کد ارائه شده، توسعه دهندگان می توانند سیستم احراز هویت امن خود را برای خود بسازند socket.io اتصالات، تنها به کاربران تایید شده اجازه می دهد تا ارتباط برقرار کنند و با سرور تعامل داشته باشند.

نوشته های مشابه

دیدگاهتان را بنویسید

نشانی ایمیل شما منتشر نخواهد شد. بخش‌های موردنیاز علامت‌گذاری شده‌اند *

دکمه بازگشت به بالا