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




الجزء الأول ،
الجزء الثاني ،
الجزء الرابعمطبات في تنفيذ تفاعل ملحقات الويب وجانب الخادم
كما هو موضح سابقًا ، يستخدم جانب الخادم Meteor.js. لتنفيذ RESTful API ، يتم استخدام حزمة
github.com/kahmali/meteor-restivus . وقد قام بالفعل بتنفيذ جزء منه لتغطية آليات المستخدم المتعلقة بالتصريح.
على سبيل المثال ، يكفي تحديد
authRequired: true ، كما هو موضح في المثال أدناه ، بحيث تعمل نقطة API للمستخدمين المصرح لهم فقط.
Api.addRoute('clientScript/:id_script', {authRequired: true}, {get: { action: function() {
وهكذا ، تمت إضافة ثلاث نقاط API للتسجيل ، لتلقي بيانات الملف الشخصي وتحديثها ، لإعادة تعيين كلمة المرور.
في امتداد الويب نفسه ، عند استدعاء طرق تتطلب الترخيص ، يتم استخدام الكود التالي:
var details = { url: API_URL + '/api/v1/clientDataRowDownload/' + dataRowId + '/download', method: 'GET', contentType: 'json', headers: {'X-Auth-Token': kango.storage.getItem("authToken"), 'X-User-Id': kango.storage.getItem("userId")} }; kango.xhr.send(details, function(data) {
مثال على طلب التفويض مرئي بوضوح هنا. تتضمن العناوين X-Auth-Token و X-User-Id ، والتي تم الحصول عليها نتيجة لعملية التسجيل أو التفويض. يتم تخزين هذه البيانات في التخزين المحلي لملحق الويب وهي متاحة دائمًا في البرنامج النصي content.js.
يتم تنزيل الملفات في امتداد الويب من خلال قراءة الملف على جانب المتصفح وإرساله عبر XHR:
$("form#uploadFile").on("submit", function(event, template) { event.preventDefault(); var reader = new FileReader(); reader.onload = function(evt) { var details = { url: API_URL + '/api/v1/clientFileAdd/' + kango.storage.getItem("userId"), method: 'POST', contentType: 'json', params: {"content": encodeURIComponent(evt.target.result.replace(/^data:[^;]*;base64,/, "")), "name": encodeURIComponent(event.currentTarget.fileInput.files[0].name), "size": event.currentTarget.fileInput.files[0].size, "type": event.currentTarget.fileInput.files[0].type, "lastModified": event.currentTarget.fileInput.files[0].lastModified }, headers: {'X-Auth-Token': kango.storage.getItem("authToken"), 'X-User-Id': kango.storage.getItem("userId")} }; kango.xhr.send(details, function(data) { if (data.status == 200 && data.response != null) { if(data.response.status == "success") {
من المهم ملاحظة السطر
event.target.result.replace (/ ^ data: [^؛] *؛ base64، /، "") . يتم ترميز الملف الموجود على جانب المستعرض في base64 ، ولكن من أجل التوافق على جانب الخادم عند استخدام هذا الترميز في سطر
Buffer.from (سلسلة جديدة (this.bodyParams.content) ، "base64") ، يجب أن نقطع بادئة التشفير ونقرأ فقط "نص" الملف . من الضروري أيضًا ملاحظة الالتفاف في encodeURIComponent ، نظرًا لأن نفس
+ غالبًا ما يوجد في base64 وأسماء الملفات.
عند تحرير البرامج النصية ، يجب مراعاة ترميز الأحرف في نص البرنامج النصي عند نقل المحتوى. في بعض الحالات ، لم ينتج عن ترميز base64 النتائج الصحيحة عند فك التشفير على جانب الخادم باستخدام encodeURIComponent. لذلك ، يتم استخدام التشفير القسري في utf8 بشكل أولي مع utf8.encode (str) ؛ حيث
mths.be/utf8js v3.0.0 منmathias
يتم تنفيذ تنزيلات الملفات باستخدام مكتبة FileSaver الراسخة. يتم نقل البيانات التي يتم تلقيها من خلال XHR ببساطة إلى مدخلات مُنشئ الملف ، ومن ثم تتم تهيئة تنزيل الملف:
var file = new File([data.response.data.join("\n")], "data_rows" + date.getFullYear() + "_" + (date.getMonth() + 1) + "_" + date.getDate() + ".csv", {type: "application/vnd.ms-excel"}); saveAs(file);
مكتبة داخلية للنصوص المخصصة
لكي يتفاعل النص البرمجي وملحق الويب وجزء الخادم ، من الضروري أن يكون لديك رابط متوسط يسمح لك بتلقي البيانات بسرعة من الملف الذي تم تنزيله ، وحفظ البيانات بعد تنفيذ البرنامج النصي ، إلخ.
لهذا الغرض ، تمت كتابة مكتبة داخلية ، تتم تهيئتها قبل أن يبدأ أي برنامج نصي في العمل بإضافة نفسه إلى رمز الصفحة. هنا تحتاج إلى إضافة معلومات حول سياسة حماية المصدر لتحميل الموارد ، وهي سياسة أمان المحتوى.
تستخدم العديد من المواقع رؤوس CSP للحماية من تنفيذ شفرة جافا سكريبت عشوائية على صفحات خدمات الويب الخاصة بها ، وبالتالي حماية نفسها من XSS على جانب متصفح الويب.
نظرًا لأن المستخدم يقوم بتثبيت امتداد الويب بمفرده ، فإنه قادر على تغيير رؤوس ومحتويات المورد الذي تم تنزيله. نظرًا لوجود خطأ في Mozilla Firefox ، فهذه مشكلة لبعض المواقع. أي أنه في امتداد الويب لمتصفح فايرفوكس لن يكون من الممكن تعديل الرؤوس أو إضافة علامة وصفية لإلغاء سياسة CSP للمواقع التي يتم استخدامها عليها. لم يتم إغلاق هذا الخطأ لعدة سنوات ، على الرغم من أن المعايير تنص بوضوح على أحكام لممتلكات الويب ، والتي تنص على أن السياسة المتعلقة بالموارد التي تم تنزيلها من جانب خادم التطبيق لا يمكن أن تكون مهيمنة فيما يتعلق بإضافات الويب التي تم تثبيتها من قبل المستخدم نفسه.
يمكن تنفيذ قيود سياسة CSP باستخدام إطار عمل kango بالطريقة التالية:
var browserObject; if(kango.browser.getName() == 'chrome') { browserObject = chrome; } else { browserObject = browser; } var filter = { urls: ["*://*/*"], types: ["main_frame", "sub_frame"] }; var onHeadersReceived = function(details) { var newHeaders = []; for (var i = 0; i < details.responseHeaders.length; i++) { if ('content-security-policy' !== details.responseHeaders[i].name.toLowerCase() && 'x-xss-protection' !== details.responseHeaders[i].name.toLowerCase() ) { newHeaders.push(details.responseHeaders[i]); } } return { responseHeaders: newHeaders }; }; browserObject.webRequest.onHeadersReceived.addListener(onHeadersReceived, filter, ["blocking", "responseHeaders"]);
في هذه الحالة ، يجب ألا تنسى إضافة خطوط في بيان ملحق الويب الذي يسمح بالعمل مع كائن webRequest في وضع الحظر:
"permissions": { ... "webRequest": true, "webRequestBlocking": true, ... }
بعد حل المشكلة بالقيود التي يفرضها CSP ، يمكن للمستخدم استخدام النصوص المكتوبة من قبله على أي صفحة على الإنترنت.
وظائف الاتصال من المكتبة الداخلية متاحة من خلال كائن Gc العالمي.
الوظائف المنفذة حاليا:
- GC.saveRow (الاسم ، المحتوى ، [rewrite = 0 ، async = false]) ؛ ، حيث يكون name هو اسم السطور التي سيتم كتابتها إلى المجموعة ، والمحتوى هو خط البيانات نفسه للكتابة ، وإعادة الكتابة هي علامة للكتابة فوق المجموعة بأكملها ، المستخدمة في استدعاء Gc.saveRow (name، 'clear'، 1)؛ الذي يحذف جميع الإدخالات في مجموعة السلاسل ، يعد المتزامن علامة للعمل في الوضع غير المتزامن.
- GC.getRows (الاسم ، الرقم ، [count = 1 ، async = false]) ؛ حيث يكون name هو اسم السطور في المجموعة ، والرقم هو الرقم التسلسلي للخط لاستقبال البيانات ، والعد هو عدد البيانات المستلمة بدءًا بالرقم ، غير المتزامن هو علامة للعمل في الوضع غير المتزامن.
- GC.console (سلسلة) ؛ ، حيث أن string عبارة عن سلسلة للإخراج لوحدة تحكم GC في الصفحة حيث يتم تنفيذ البرنامج النصي. على سبيل المثال ، لتوضيح تقدم المهمة.
- GC.clearConsole () ؛ ، تقوم الوظيفة بمسح وحدة تحكم GC في الصفحة حيث يتم تنفيذ البرنامج النصي.
- GC.stopScript () ؛ ، وظيفة لإيقاف تنفيذ البرنامج النصي.
- GC.loadFile (name، [parseAs = text]) ؛ ، حيث name هو اسم الملف مع الملحق الذي تريد الحصول على محتوياته ، parseAs هو تنسيق المعالج المسبق للبيانات ، يتم دعم json والنص حاليًا.
في المقالة التالية سأتحدث عن "
المهام المجدولة ".