كيفية تعيين API REST المستندة إلى Symfony مع OAuth

لذلك ، تريد تعيين واجهة برمجة تطبيقات REST على أساس Symfony ... اسمح لنا بتقديم بعض النصائح حول كيفية المتابعة.

SensioLabs ، منشئو Symfony ، يصفونها بأنها "مجموعة من مكونات PHP ، وإطار تطبيق ويب ، وفلسفة ، ومجتمع - جميعهم يعملون معًا في وئام. من الطبيعي أن تكون حريصًا على البدء به! سيوضح لك هذا البرنامج التعليمي كيفية تعيين API REST المستندة إلى Symfony باستخدام بروتوكول OAuth كبروتوكول ترخيص.

اهم الاشياء اولا

ستحتاج إلى تثبيت بيئة Symfony. أدلة الوثائق الرسمية حول هذه العملية وتشرح كيفية حل القضايا المشتركة.

استخدمنا أحدث إصدار ثابت في الوقت الذي نكتب فيه ، وهو Symfony 3.3.10. أيضًا ، قد ترغب في تعيين Apache أو Nginx VirtualHost ، بحيث يمكنك الوصول إلى التطبيق الخاص بك من خادم الويب الخاص بك. أفضل طريقة للقيام بذلك هي متابعة المستندات الرسمية.

وحاول الوصول إلى التطبيق الخاص بك عن طريق الاتصال بالمضيف في المتصفح:

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

دعونا نتعامل مع بعض المتطلبات

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

يتطلب الملحن $ friendsofsymfony / بقية الحزمة

ثم قم بإضافة الحزمة إلى تطبيقك / AppKernel.php:

<؟ PHP
الطبقة AppKernel يمتد Kernel
{
    سجل الوظائف العامة
    {
        حزم $ = [
            // ...
            جديد FOS \ RestBundle \ FOSRestBundle () ،
        ].
        // ...
    }
}

نظرًا لأننا سنحتاج إلى التعامل مع تسلسل المحتوى وإلغاء التسلسل ، ستحتاج أيضًا إلى استخدام JMSSerializerBundle

ابدأ بإنشاء حزمة

الشيء التالي الذي سنفعله هو إنشاء حزمة حيث يمكننا ضبط جهاز التحكم REST وطرقنا ، ودعونا نسميها MySuperRestBundle:

إنشاء حاوية / وحدة تحكم $ php: حزمة - مساحة الاسم = cleverti / MySuperRestBundle - لا يوجد تفاعل

الآن حان الوقت لوحدة التحكم

الآن بعد إنشاء الحزمة الخاصة بنا ، سنحتاج إلى وحدة تحكم أيضًا:

إنشاء حاوية / وحدة تحكم $ php: تحكم - لا تفاعل - تحكم = cleverti \ MySuperRestBundle: بقية

سيؤدي هذا إلى إنشاء فئة وحدة التحكم في src / cleverti / MySuperRestBundle / Controller / RestController.php ، وهو المكان الذي سنركز عليه الآن.

سيقوم أمر إنشاء: controller بإنشاء هيكل أساسي لفئة وحدة التحكم الخاصة بك فقط ، مما يعني أنك ستحتاج إلى إضافة جميع الآليات لوحدة التحكم الخاصة بك للرد على المسارات التي تريد تعريفها ، ويتم ذلك باستخدام الإجراءات.

بشكل افتراضي ، تبدو وحدة التحكم لدينا كما يلي:

<؟ PHP
مساحة الاسم cleverti \ MySuperRestBundle \ Controller؛
استخدام Symfony \ Bundle \ FrameworkBundle \ Controller \ Controller؛
استخدام Sensio \ Bundle \ FrameworkExtraBundle \ Configuration \ Route؛
الطبقة RestController يمتد تحكم
{
}

... مما يعني أننا سنحتاج إلى إجراء بعض التغييرات. بعد كل شيء ، هذا هو مجرد الهيكل العظمي الأولي.

لذلك ، لنبدأ بتحديد طريق لوحدة التحكم الخاصة بنا.

يمكنك تعيين بادئة لوحدات التحكم الخاصة بك ، وهو أمر مفيد للغاية إذا كنت تريد أن يكون لديك الكثير من الطرق تحت نفس وحدة التحكم ، وعدة وحدات تحكم تحت نفس الحزمة. لذلك يجب عليك إضافة ما يلي في التطبيق / config / routing.yml:

cleverti_my_super_rest_bundle:
    مورد: "@ clevertiMySuperRestBundle / Resources / config / routing.yml"
    بادئة: / api

في هذه الحالة ، ستكون البادئة لدينا هي / api. ثم نحتاج إلى إخبار Symfony بأن وحدة التحكم الخاصة بنا ستتوقع طلب REST ، لذلك يجب علينا إضافة ما يلي في src / cleverti / MySuperRestBundle / Resources / config / routing.yml:

cleverti_my_super_rest_controller:
    النوع: الراحة
    مورد: cleverti \ MySuperRestBundle \ Controller \ RestController

نحن الآن على استعداد لتحديد طرقنا في جهاز التحكم لدينا. نظرًا لأننا سنستخدم REST ، فلن تقوم أداة التحكم الخاصة بنا بتوسيع وحدة التحكم الافتراضية في Symfony ، ولكن FOSRestController بدلاً من ذلك.

لذلك ، دعونا نحدد إجراء اختبار لنا لرؤية سحر واجهة برمجة التطبيقات في العمل ، وسنسميها restGetAction في هذه الحالة. سنستخدم أيضًا تعليقًا توضيحيًا لضبط مسارنا ، مثل هذا المسار:Get ("/ get / cleverti").

سيحدد هذا الطريق كلاً من طريقتك ومسار الاتصال. نظرًا لتعيين وحدة التحكم هذه على البادئة / api ، فستحتاج إلى الاتصال بها بواسطة: GET / api / get / cleverti. يجب أن نحصل على شيء مثل هذا في وحدة التحكم لدينا:

<؟ PHP
مساحة الاسم cleverti \ MySuperRestBundle \ Controller؛
استخدام FOS \ RestBundle \ Controller \ FOSRestController ؛
استخدام Symfony \ Component \ HttpFoundation \ Request؛
استخدم FOS \ RestBundle \ Controller \ Annotations \ Get؛
الطبقة RestController يمتد FOSRestController
{
    / **
     * هنا يذهب طريقنا
     * @ Get ("/ get / cleverti")
     * /
    الوظيفة العامة restGetAction (طلب $ request)
    {
        / / قم بعمل شيء باستخدام كائن الطلب الخاص بك
        بيانات $ = صفيف (
            "name" => "cleverti" ،
            "إضافي" => "رائع!"
        )؛
        $ view = $ this-> view (بيانات $ ، 200) ؛
        إرجاع $ this-> handleView ($ view) ؛
    }
}

ونحن على استعداد تقريبا.

نحتاج فقط إلى تعيين بعض الإعدادات الأساسية لجعل FosRestBundle للاستماع لمكالمات REST ، وبما أننا سنستخدم محرك العرض الخاص به ، فلن نحتاج إلى تعيين أي قالب غصين لردودنا. لذلك ، أضف ما يلي إلى التطبيق / config / config.yml:

fos_rest:
    routing_loader:
        default_format: json
    رأي:
        view_response_listener: صحيح

واصل الآن ، واتصل بمسارك مع عميل REST المفضل لديك ، أو لأن الطريقة المحددة لهذا المسار هي GET ، يمكنك أيضًا الاتصال بها من متصفح الويب الخاص بك. استخدمنا الأرق كعميل REST في مثالنا.

والنجاح!

خارج API يستمع إلى طلبك ، والإجابات مع استجابة JSON ، تماما كما تم تعريفه. والآن تسألني: ماذا عن التحكم في الوصول؟ لا أريد أن تكون معظم أساليب واجهة برمجة التطبيقات الخاصة بي متاحة للعامة ...

إعداد OAuth

هناك عدة طرق ترخيص واحدة من أكثرها استخدامًا هي OAuth 2.0. يتيح لك استخدام المصادقة مع موفر خارجي ، وهو نوع رائع إذا كنت تنوي استخدام Twitter أو Facebook أو موفر آخر لمستخدميك لتعريف أنفسهم. في مثالنا ، سوف نستخدم FOSUserBundle كموفر للمستخدم ، لذلك ، دعونا ننشئ حزمتين ، واحدة لكيان FOSUserBundle ، وحزمة أخرى لكيانات FOSOAuthServerBundle التي نحتاج إلى ضبطها أثناء عملية التثبيت:

إنشاء حاوية / وحدة تحكم $ php: حزمة - مساحة الاسم = cleverti / UserBundle - لا يوجد تفاعل
إنشاء حاوية / وحدة تحكم $ php: حزمة - مساحة الاسم = cleverti / OAuthBundle - لا يوجد تفاعل

ثم ، يجب عليك تثبيت FOSUserBundle كما هو موضح في الوثائق الرسمية.

ثم قم بتثبيت FOSOAuthServerBundle كما هو موضح هنا.

أيضًا ، لا تنسَ إتاحة / api للمستخدمين المصادق عليهم فقط ، وذلك بإضافته في كتلة الوصول_التحكم ، كما يلي:

الأمان:
    ...
    صلاحية التحكم صلاحية الدخول:
        - {path: ^ / api ، الأدوار: [IS_AUTHENTICATED_FULLY]}

سيكون قسم المسار هو مسار مساراتك ، جزئيًا أو مطلقًا ، الذي تريد حمايته. وجزء الأدوار هو المكان الذي تحدد فيه الأدوار التي يجب أن يمتلكها المستخدمون لديك ، حتى تتمكن من الوصول إلى الطرق. نظرًا لأن هذا مجرد مثال على أنني لن أركز على الأدوار ، فأنا أعرّف فقط أن أي مستخدم مصادق قد يكون له حق الوصول ، ولكن ليس مستخدمًا مجهولًا. يمكنك معرفة المزيد عن الأدوار هنا. هكذا يبدو تطبيقي / config / config.yml:

الواردات:
    - {مورد: parameter.yml}
    - {resource: security.yml}
    - {resource: services.yml}
    - {resource: "@ clevertiMySuperRestBundle / Resources / config / services.yml"}
    - {resource: "@ clevertiOAuthBundle / Resources / config / services.yml"}
    - {resource: "@ clevertiUserBundle / Resources / config / services.yml"}
المعلمات:
    الإعدادات المحلية: en
الإطار:
    # إيسي: ~
    #translator: {fallbacks: ['٪ locale٪']}
    سر: '٪ سر ٪'
    التوجيه:
        مورد: '٪ kernel.project_dir٪ / app / config / routing.yml'
        متطلبات صارمة: ~
    النموذج: ~
    csrf_protection: ~
    التحقق من الصحة: ​​{enable_annotations: true}
    #serializer: {enable_annotations: true}
    النموذجيه:
        المحركات: ['غصين']
    default_locale: '٪ locale٪'
    Trust_hosts: ~
    جلسة:
        # https://symfony.com/doc/current/reference/configuration/framework.html#handler-id
        handler_id: session.handler.native_file
        save_path: '٪ kernel.project_dir٪ / var / session /٪ kernel.environment٪'
    شظايا: ~
    http_method_override: صحيح
    الأصول: ~
    php_errors:
        سجل: صحيح
# غصين التكوين
غصين:
    تصحيح: '٪ kernel.debug٪'
    rict_variables: '٪ kernel.debug٪'
# تكوين العقيدة
عقيدة:
    dbal:
        سائق: pdo_mysql
        المضيف: '٪ database_host٪'
        المنفذ: '٪ database_port٪'
        dbname: '٪ database_name٪'
        المستخدم: '٪ database_user٪'
        كلمة المرور: '٪ database_password٪'
        محارف: UTF8
    مكتب إدارة السجلات:
        auto_generate_proxy_classes: '٪ kernel.debug٪'
        naming_strategy: doctrine.orm.naming_strategy.underscore
        رسم تلقائي: صحيح
# Swiftmailer التكوين
swiftmailer:
    النقل: '٪ mailer_transport٪'
    المضيف: '٪ mailer_host٪'
    اسم المستخدم: '٪ mailer_user٪'
    كلمة المرور: '٪ mailer_password٪'
    التخزين المؤقت: {type: memory}
# FosRestBundle التكوين
fos_rest:
    routing_loader:
        default_format: json
    رأي:
        view_response_listener: صحيح
# FOSUserBundle التكوين
fos_user:
    db_driver: orm
    firewall_name: رئيسي
    user_class: cleverti \ UserBundle \ Entity \ User
    من البريد الإلكترونى:
        العنوان: "٪ mailer_user٪"
        sender_name: "٪ mailer_user٪"
# FOSOAuthServerBundle التكوين
fos_oauth_server:
    db_driver: orm
    client_class: cleverti \ OAuthBundle \ Entity \ Client
    access_token_class: cleverti \ OAuthBundle \ Entity \ AccessToken
    refresh_token_class: cleverti \ OAuthBundle \ Entity \ RefreshToken
    auth_code_class: cleverti \ OAuthBundle \ Entity \ AuthCode
    الخدمات:
        user_provider: fos_user.user_provider.username
        خيارات:
            access_token_lifetime: 86400
            refresh_token_lifetime: 1209600
            auth_code_lifetime: 30

وهذا هو تطبيقي / config / security.yml:

الأمان:
    الترميز:
        FOS \ UserBundle \ Model \ UserInterface: bcrypt
        
    role_hierarchy:
        ROLE_ADMIN: ROLE_USER
        ROLE_SUPER_ADMIN: ROLE_ADMIN
    مقدمي:
        في الذاكرة:
            الذاكرة: ~
        fos_userbundle:
            id: fos_user.user_provider.username
            
    الجدران النارية:
        ديف:
            نمط: ^ / (_ (profiler | wdt) | css | images | js) /
            الأمن: خطأ
            
        oauth_token:
            نمط: ^ / oauth / v2 / الرمز المميز
            الأمن: خطأ
        oauth_authorize:
            نمط: ^ / oauth / v2 / المصادقة
            form_login:
                المزود: fos_userbundle
                check_path: / oauth / v2 / auth_login_check
                login_path: / oauth / v2 / auth_login
                use_referer: صحيح
        المعهد:
            نمط: ^ / api
            fos_oauth: صحيح
            عديمي الجنسية: صحيح
            مجهول: خطأ
        الأساسية:
            نمط: ^ /
            form_login:
                المزود: fos_userbundle
                csrf_token_generator: security.csrf.token_manager
            
            مجهول: صحيح
    صلاحية التحكم صلاحية الدخول:
        - {path: ^ / api ، الأدوار: [IS_AUTHENTICATED_FULLY]}

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

عندما ننتهي من تثبيت FOSUserBundle و FOSOAuthServerBundle ، سنحتاج إلى إنشاء عميل OAuth ومستخدم ، حتى نتمكن من إنشاء رموز الوصول إلى Oauth. سنحتاج ذلك لطلبنا.

لذلك ، قم بإنشاء الأمر التالي في src / cleverti / OAuthBundle / Command / ClientCreateCommand.php:

<؟ PHP
مساحة الاسم cleverti \ OAuthBundle \ Command؛
استخدام Symfony \ Bundle \ FrameworkBundle \ Command \ ContainerAwareCommand؛
استخدام Symfony \ Component \ Console \ Input \ InputArgument؛
استخدام Symfony \ Component \ Console \ Input \ InputInterface؛
استخدم Symfony \ Component \ Console \ Input \ InputOption؛
استخدام Symfony \ Component \ Console \ Output \ OutputInterface؛
الطبقة ClientCreateCommand يمتد ContainerAwareCommand
{
    تكوين وظيفة محمية ()
    {
        $ هذا
            -> setName ( 'أوث: العميل: إنشاء')
            -> setDescription ("إنشاء عميل جديد")
            -> addOption ('redirect-uri' ، null ، InputOption :: VALUE_REQUIRED | InputOption :: VALUE_IS_ARRAY ، 'Set uri redirect. استخدم عدة مرات لتعيين uris متعددة.' ، null)
            -> addOption ('نوع المنحة' ، فارغة ، InputOption :: VALUE_REQUIRED | InputOption :: VALUE_IS_ARRAY ، 'تعيين نوع المنحة المسموح به. استخدم عدة مرات لتعيين أنواع منح متعددة' ، فارغة)
        .
    }
    تنفيذ وظيفة محمية (إدخال $ InputInterface ، إخراج OutputInterface $)
    {
        $ clientManager = $ this-> getContainer () -> get ('fos_oauth_server.client_manager.default')؛
        $ client = $ clientManager-> createClient ()؛
        $ client-> setRedirectUris ($ الإدخال-> getOption ( 'إعادة توجيه-أوري'))؛
        $ client-> setAllowedGrantTypes ($ الإدخال-> getOption ( 'منحة من نوع'))؛
        $ clientManager-> updateClient ($ العميل).
        $ output-> writeln ("تمت إضافة عميل جديد له معرف عام ". $ client-> getPublicId (). " وسرية ". $ client-> getSecret (). " ")؛
    }
}

قم بإنشاء عميل OAuth عن طريق تشغيل الأمر الذي أنشأته للتو من خلال خيارين - إعادة توجيه URIs التي ترغب في استخدامها ، وأنواع المنح التي تريد السماح لهذا العميل باستخدامها ، والتي يجب أن توفر لك مخرجات كما يلي:

php bin / console oauth: client: create - reirect-uri = http: //www.cleverti.com - grant-type = password --grant-type = refresh_token
تمت إضافة عميل جديد باستخدام معرّف عام

ويمكنك استخدام الأمر الافتراضي الذي توفره FOSUserBundle لإنشاء مستخدم:

php bin / console fos: user: create ricardo.correia ricardo.correia@cleverti.com password

الآن دعونا نحاول طلب رمز دخول ، عن طريق تقديم طلب POST إلى مسار OAuth / oauth / v2 / الرمز المميز. يتوقع هذا المسار grant_type باستخدام كلمة مرور القيمة ، و client_id و client_secret ، واسم المستخدم وكلمة المرور للمستخدم:

{
    "grant_type": "password" ،
    "client_id": "1_4654cpi0zu0wokk4g04gk0w444wkkwcs4sg0okoo0gks0gcokg" ،
    "client_secret": "1h7xdjjo6ixwwow0w00oss4sgc0w8o48ocgw808w0gg4s40owc" ،
    "اسم المستخدم": "ricardo.correia" ،
    "كلمة المرور": "كلمة المرور"
}

إذا قدمنا ​​طلبًا من عميل REST لدينا ، فسوف نحصل على النتيجة التالية:

حسنًا ، كل شيء يسير على ما يرام حتى الآن ، وحصلنا على رمز وصولنا.

سنحتاج الآن إلى استخدام رمز الدخول الخاص بنا في جميع طلباتنا ، من خلال تعيين رأس التفويض ، على النحو التالي:

التفويض: Bearer MTI4ZmUyNTg1ZDgwM2Y0ZmJlZjg3OWNlNTA2ZDE5ZTk4ZTQzZGMzNjllOGE4YWI5Yzc0ZWQxMWQ1MjVjNmY5MA

لذلك ، دعونا نحاول الوصول إلى طريقنا المحمي / api / get / cleverti ، لكن هذه المرة باستخدام رأس التفويض:

نجاح! طريقنا محمي ومتاح للمستخدمين المصادق عليهم فقط ، باستخدام OAuth 2.0. وهذا إلى حد كبير.

أيضًا ، قد ترغب في تحديث رمز الوصول المميز عندما تنتهي مدة صلاحيته ، ويتم ذلك باستخدام / oauth / v2 / الرمز المميز المسار أيضًا ، ولكن هذه المرة ستحتاج إلى إرسال نوع المنحة كـ refresh_token و client_id و client_secret ، refresh_token بدلاً من المستخدم وكلمة المرور:

{
    "grant_type": "refresh_token" ،
    "client_id": "1_4654cpi0zu0wokk4g04gk0w444wkkwcs4sg0okoo0gks0gcokg" ،
    "client_secret": "1h7xdjjo6ixwwow0w00oss4sgc0w8o48ocgw808w0gg4s40owc" ،
    "refresh_token": "OWQyNTI1MDk4OTlhZTk0NDdlYzA0YTM4ZTFlZDZjMmQxNDY3OTBjMzBiNTYzOWMwMTgxN2UwMGYwYWE3M2RiYw"
}

وستؤدي هذه المكالمة إلى إرجاع access_token جديد و refresh_token جديد:

وهذا كل شيء الآن. من الجيد أن تذهب! إذا كنت بحاجة ، فيمكنك أيضًا الوصول إلى مشروع Git التجريبي هنا.

نأمل أن تستمتع الترميز الخاص بك بقدر ما نتمتع به. إستمتع!

كتبها ريكاردو كورييا | المطور في Cleverti

تم نشر هذا المقال في الأصل على مدونة Cleverti