سؤال

لقد قمت بوضع كافة رموز مصادقة المستخدم الخاصة بي في مكان واحد، وهو lib/auth.rb.تبدو هكذا:

ليب/auth.rb

module Admin

  def do_i_have_permission_to?(permission)
    # Code to check all of this goes here
  end

end

أقوم بتضمين هذه الوحدة كجزء من مساعد التطبيق، بحيث تتوفر هذه الوظائف في جميع طرق العرض:

application_helper.rb

require 'auth'
module ApplicationHelper
  include Admin
  # other stuff here
end

وقمت أيضًا بتضمينها كجزء من وحدة التحكم في التطبيق، بحيث يمكن لوحدات التحكم أيضًا استدعاء الوظائف:

application.rb

require 'auth'
class ApplicationController < ActionController::Base
  include Admin
end

حتى الان جيدة جدا.

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

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

Admin::do_i_have_permission_to?(permission)

يحتاج للوصول إلى معلمات الطلب الحالية.لا يمكنني فقط استخدام المعلمات[:foo] كما أفعل في وحدة التحكم، لأنه لم يتم تعريف المعلمات؛بالمثل، request.parameters[:foo] لن يعمل أيضًا.لقد كشف بحثي:

  • معلمات البحث الحالية موجودة في الطلب الحالي،
  • الطلب الحالي موجود في وحدة التحكم الحالية،
  • وحدة التحكم الحالية موجودة في المرسل الحالي، و
  • لست متأكدًا من أن المرسل الحالي موجود في أي مكان.

ومع ذلك، تخبرني التجربة أنه عندما أقفز عبر هذه الأطواق العديدة، فمن المحتمل جدًا أن أفعل ذلك بشكل خاطئ.إذن ما هي الطريقة الصحيحة للقيام بذلك؟الخيارات التي فكرت فيها هي:

  • ما عليك سوى نقل جميع الوظائف الموجودة حاليًا في auth.rb إلى ApplicationHelper حيث (على ما أعتقد) سيكون لديهم إمكانية الوصول إلى الطلب وما إلى ذلك.يعمل، ولكن تشوش الجحيم من المساعد.
  • انقل جميع الوظائف إلى مكان آخر وسوف يرون تلك الطرق (لا أعرف أين)
  • أنا فقط أفتقد شيئًا ما.
هل كانت مفيدة؟

المحلول

في تطبيق Rails النموذجي، يتم تخزين معلومات المصادقة في الجلسة النشطة، وليس المعلمات.على هذا النحو، من السهل جدًا كتابة مساعد يفعل ما تريد.

يبدو من غير التقليدي إنشاء وحدة يتم تضمينها بعد ذلك في ApplicationHelper.يتمثل النهج التقليدي في إنشاء مساعد منفصل والذي من المحتمل أن يُطلق عليه في هذه الحالة اسم AuthenticationHelper.ويمكن بعد ذلك تضمين ذلك في أي وحدات تحكم مطلوبة، أو إذا كنت تفضل ذلك، يمكنك تحميلها في ApplicationController لإتاحتها عالميًا.

بشكل عام، يجب ألا يتضمن المساعدون مساعدين آخرين.من الأفضل تحميل عدة مساعدين ببساطة في وحدة تحكم معينة.

تتمتع الأساليب المساعدة بإمكانية الوصول الكامل إلى أي متغيرات مثيل معلنة ضمن سياق وحدة التحكم التي تعمل منها.على وجه التحديد، هذه هي متغيرات الحالة فقط (@name) وليست متغيرات محلية (اسم).يتم تنفيذ الأساليب المساعدة لطريقة عرض معينة أيضًا.

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

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

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

في حين أنه من المغري استخدام كومة كاملة منها مثل can_edit_user؟وcan_create_group؟هذه الأمور تخرج عن نطاق السيطرة بسرعة كبيرة.هل هو تصميم أبسط يمكن وضعه في خطاف للأغراض العامة؟أو has_authority_to؟الطريقة التي تم تمرير العملية وأي معلمات مطلوبة.

على سبيل المثال، تنفيذ تقريبي للغاية:

  class ApplicationController < ActionController::Base
  protected
    def has_authority_to?(operation, conditions = { })
      AuthenticationCheck.send(operation, conditions)
    rescue
      false
    end
  end

  module AuthenticationCheck
    def self.edit_user?(conditions)
      session_user == conditions[:user]
    end
  end

  class UserController
    # ...

    def edit
      @user = User.find(params[:id])

      unless (has_authority_to?(:edit_user, :user => @user))
        render(:partial => 'common/access_denied', :status => :forbidden)
      end
    rescue ActiveRecord::RecordNotFound
      render(:partial => 'users/not_found')
    end
  end

من الواضح أنك تريد إجراء الكثير من عمليات التحقق من السلطة في كتل before_filter لتجنب التكرار وتعزيز الاتساق.

قد يكون مثال الإطار الكامل مفيدًا أكثر، مثل نظام مصادقة مستخدم سوار المعصم:

http://github.com/theworkinggroup/wristband/tree/master

مرخصة بموجب: CC-BY-SA مع الإسناد
لا تنتمي إلى StackOverflow
scroll top