نظاما تسجيل حساب جديد وتسجيل الدخول

فراس اللومنذ 5 سنوات

بعدما انتهينا من قراءة البيانات من المُستخدم، نحتاج لإتمام نظام تسجيل حساب جديد لاختبار وجود حساب سابق بنفس اسم المُستخدم أو البريد الإلكتروني، وهنا سوف نعتمد على مكتبة PassportJS التي توفّر مجموعة كبيرة من الأدوات الجاهزة، والتي تُتيح للمُطوّر إضافة أنظمة تسجيل دخول مُختلفة بسهولة تامّة.

في واجهات صراحة البرمجية لن نعتمد لا على فيسبوك ولا على تويتر، إنما فقط على البريد الإلكتروني وكلمة المرور لأن مُستخدم موقع صراحة يبحث عن حماية خصوصيّته، واستخدام حسابات الشبكات الاجتماعية قد يُثير الريبة في نفسه، وبالتالي يبتعد عن استخدام الموقع.

مُلاحظة: سبق وأن شرحت بالتفصيل في درس مُنفصل آلية استخدام مكتبة passportJS للراغبين بالخوض بجميع التفاصيل، وهو موجود على الرابط التالي.

قبل البدء في أي شيء، نحتاج لإتمام عملية تسجيل حساب جديد، ففي الدرس السابق وقفنا عند اختبار البيانات الواردة للتأكّد من أنها مكتوبة بشكل صحيح. سنقوم بتعديل طفيف، فنحن نحتاج إلى اسم المُستخدم، البريد الإلكتروني، إضافة إلى كلمة المرور والاسم الحقيقي. بعدها سنقوم بإنشاء دالّة للتأكّد من أن تلك البيانات غير موجودة في قواعد البيانات، وفي حالة كان كل شيء على ما يُرام تتم عملية إنشاء الحساب ثم تسجيل الدخول بنجاح.

نستبدل app.post المسؤولة عن المسار /register بالكود التالي في index.js

app.post("/register", async (req, res)=>{
    var email = req.body.email
    var password = req.body.password
    var passwordConf = req.body.passwordConf
    var username = req.body.username 
    var realname = req.body.realname

    if (email != undefined && validator.isEmail(email)) {
        if ((password == undefined || passwordConf == undefined)
        || (validator.isEmpty(password) || validator.isEmpty(passwordConf))
         || (password != passwordConf)
         || (username == undefined || realname == undefined)
        ) { 
            res.send("يُرجى التأكّد من تطابق كلمات المرور ومن تعبئة جميع الحقول.")
        }else{
            //إتمام طلب تسجيل حساب جديد
            let userdata = {
                "email":email,
                "password":md5(password),
                "username":username,
                "realname": realname,
            }
            try {
                var checkOrSignUp = await dbConfig.signUp(userdata)
                res.send(checkOrSignUp)
            }catch(e){
                res.send(e)
            }
        }
    }else {
        res.send("خطأ في البريد الإلكتروني");
    }
})

ونقوم بتثبيت مكتبة md5 عبر الكود التالي

npm install md5 --save

دون نسيان استدعائها في أعلى index.js بالشكل التالي

var md5 = require("md5")

الآن نأتي لشرح ما حدث في الدالة الجديدة، بداية نلحظ كلمة async، وهذا لنُخبر المُترجم (المُفسّر) Compiler أننا سوف نستخدم async/await لأننا سوف نستعلم من قاعدة البيانات ونحتاج لانتظار النتيجة قبل تنفيذ أي شيء آخر. لمزيد عن الوعود Promises وasync/await يمكنكم مُراجعة الدرس التالي.

أضفنا كذلك حقلين واحد لاسم المُستخدم وآخر للاسم الحقيقي، وفي حالة إرسال جميع البيانات قُمنا بإنشاء كائن جديد يحتوي على جميع البيانات الواردة بشكل مُنظّم مع تشفير كلمة المرور باستخدام md5. لا تقم بتخزين كلمات المرور كما هي في قاعدة البيانات، شفّرها وقم بحفظها. أخيرًا. قُمنا بطلب الدالة signUp الموجودة ضمن ملف database.js.

أحطنا الاستدعاء بـ try/catch لأننا نتعامل مع الوعود، فالدالة ستقوم بأخذ البيانات المُرسلة للتأكّد من عدم وجودها في قاعدة البيانات، في حالة وجودها سترفض الطلب، وفي حالة عدم وجودها ستقوم بإتمام عملية تسجيل الدخول.

الدالة الجديدة ضمن ملف database.js من الشكل

async function signUp(userdata) {
  return new Promise((onResolve, onReject)=>{
    MongoClient.connect(dbURL, async function(err, client) {
      assert.equal(null, err);
      const db = client.db(dbName);
    var checkuser =  await db.collection("users").count({
      $or: [{
        "email": userdata["email"]
    }, {
        "username": userdata["username"]
    }]
  })
     if (checkuser == 0 ) {
        await db.collection("users").insertOne(userdata)
       onResolve("تم إنشاء حساب جديد بنجاح")
       
     }else {
      onReject("اسم المُستخدم أو البريد الإلكتروني مُستخدمين سابقًا.")
      
     }
      client.close();
    });
  })
}

// دالة تسجيل الدخول 
async function login(email, password) {
  return new Promise((onResolve, onReject)=>{
    MongoClient.connect(dbURL, async function(err, client) {
      assert.equal(null, err);
      const db = client.db(dbName);
    db.collection("users").findOne({"email": email, "password": md5(password)}, (err, results) =>{
      if (err) onReject(err)
      onResolve(results)
    })
      client.close();
    });
  })
}

وفيها قُمنا بالبحث داخل قاعدة البيانات عن مُستخدم بنفس الإيميل المُرسل أو بنفس اسم المُستخدم. كمُطوّر يُمكنك اختبار البريد الإلكتروني فقط، أو اسم المُستخدم، أو حتى الاسم الحقيقي، كل شيء متروك لك. لكننا في هذه الحالة نرغب بأن يكون البريد غير مُسجّل، واسم المُستخدم غير موجود، لأن ترك الرسائل يكون عبر اسم المُستخدم كما سنُلاحظ فيما بعد.

أما دالة تسجيل الدخول login فهي ستقوم بالبحث ضمن المُستخدمين عن واحد يستخدم الإيميل وكلمة المرور المُرسلة، وفي حالة وجوده، نقوم بإتمام عملية تسجيل الدخول.

مُلاحظة: في ملف database.js قُم باستدعاء مكتبة md5 في الأعلى.

في نهاية ملف database، نحتاج لتصدير الدالة الجديدة، ليكون السطر الأخير على الشكل

module.exports = {testDatabaseConnection,signUp, login}

نأتي الآن لإتمام عملية تسجيل الدخول، وهذا عبر طلب الرابط /login في الموقع، وسوف نعتمد على مكتبة passport.js مثلما ذكرت في الأول. بداية، نحتاج لتثبيت المكتبة ومكتبات أُخرى لإدارة الجلسات Sessions والكوكيز Cookies، وهذا عبر الدخول إلى مُجلّد التطبيق ثم نقوم بكتابة الأوامر التالية

npm install passport --save
npm install cookie-parser --save
npm install express-session --save

نقوم بإنشاء ملف جديد باسم passport.js ونقوم بلصق الكود التالي بداخله

let loginStrategy = require("passport-local").Strategy // دلالة على الرغبة في استخدام نظام تسجيل دخول محلّي لا يعتمد على خوادم خارجية
let dbConfig = require("./database") // ملف الدوال الخاصّة بقواعد البيانات


module.exports = function(passport) {

    // دوال مُساعدة لتخزين الجلسات وإدارتها
    passport.serializeUser(function(user, done) {
        done(null, user);
      });
      
         // دوال مُساعدة لتخزين الجلسات وإدارتها
      passport.deserializeUser(function(user, done) {
        done(null, user);
      });

       passport.use(new loginStrategy({
        usernameField: 'email',
        passwordField: 'password',
        passReqToCallback: true,
        },async function(req, email, password, done){
          let checkLogin =  await dbConfig.login(email, password)
          if (checkLogin == null) {
            return done(null, false)
          }else {
            return done(null, checkLogin)   
          }
        }))
    
}

قُمنا بتعريف استرتيجية تسجيل الدخول المحلّي، أي عبر قاعدة بيانات موجودة على الخادم دون الاعتماد على شبكات التواصل الاجتماعي، وتعريف هذه الاستراتيجية موجود أساسًا ضمن مستندات مكتبة passport، فالمُطوّر بحاجة لتعريف الاستراتيجية بهذا الشكل مع تحديد حقل اسم المُستخدم (البريد الإلكتروني في حالتنا) وكلمة المرور، ثم تحديد مصير تلك البيانات، إلى أين ستُرسل ومصير النتيجة كإرسال خطأ في حالة عدم وجودها، أو نجاح في حالة وجودها. لمزيد من التفاصيل بإمكانكم زيارة صفحة توثيق المكتبة، أو مراجعة الدرس الذي كتبته حول استخدام المكتبة.

أخيرًا، نعود للملف الرئيسي index.js ونقوم بإضافة التعريفات التالية مع الانتباه لمكانها المُناسب

// في الأعلى مع بقيّة الاستداعاءات

var passport = require("passport")
let session = require('express-session')
let cookieParser = require("cookie-parser")

// مع بقيّة الـ Middlewares
app.use(cookieParser())
app.use(bodyParser.urlencoded({ extended: true }))  //  موجودة سابقًا لدينا
app.use(bodyParser.json()) //  موجودة سابقًا لدينا
app.use(session({secret: "sarahah", saveUninitialized:true, resave:false}))
app.use(passport.initialize())
app.use(passport.session());

require("./passport")(passport)

الآن نقوم بتعديل المسار الافتراضي عند طلب localhost:3033/ ونُضيف مسار لتسجيل الدخول

app.get("/", (req, res)=> {
    if (req.isAuthenticated())
    {
    res.send("أهلًا وسهلًا يا " + req.user["realname"])
    }else{
        res.send("أهلًا وسهلًا، يُمكنكم تسجيل حساب جديد لبدء استخدام الموقع")
    }
})

app.post("/login", passport.authenticate("local",{successRedirect:"/", failureRedirect:"/"}), (req, res)=>{
        res.send("تم تسجيل الدخول بنجاح، أهلًا " + req.user["realname"])
})

المسار الرئيسي يقوم باختبار قيمة الدالة isAuthenticated، وهي دالة خاصّة بمكتبة passport قيمتها Bool، وفي حالة كانت true فهذا يعني أن المستخدم قام بتسجيل الدخول سابقًا ونقوم بطباعة اسمه. أما المسار /login فهو عبر عملية post مع استخدام middleware دالة وسيطة خاصّة بـ passport، في حالة النجاح أو الفشل نقوم بتحويل المُستخدم للصفحة الرئيسية localhost:3033/ وهناك يتم التأكّد من قيمة isAuthenticated.

الآن قم بتشغيل postman، وقُم بتسجيل حساب جديد عبر الرابط localhost:3033/register كما تحدّثنا في الدرس الثاني مع تمرير اسم المُستخدم username وrealname أيضًا. وبعد إتمام عملية التسجيل، نقوم بتسجيل الدخول.

 

التعليقات (0)

لايوجد لديك حساب في عالم البرمجة؟

تحب تنضم لعالم البرمجة؟ وتنشئ عالمك الخاص، تنشر المقالات، الدورات، تشارك المبرمجين وتساعد الآخرين، اشترك الآن بخطوات يسيرة !