2016-02-18 12 views
7

रिकॉर्ड के लिए, मैं पीएचपीएसटॉर्म के साथ एक वाग्रेंट बॉक्स में PHP 7.0.0 का उपयोग कर रहा हूं। ओह, और सिम्फनी 3.सिम्फनी - एपीआई टोकन के साथ प्रमाणीकरण - अनुरोध टोकन उपयोगकर्ता शून्य

मैं API Key Authentication दस्तावेज़ीकरण का पालन कर रहा हूं। मेरा लक्ष्य है:

  • जाहिर
  • डेवलपर एक नियंत्रक में $request->getUser() लिखने के लिए अनुमति देने के लिए डेवलपर प्रोफाइलर आदि को छोड़कर उपयोगकर्ता किसी भी मार्ग के लिए प्रमाणित करने के लिए एक प्राप्त apiKey पैरामीटर के रूप में एक महत्वपूर्ण प्रदान करने के लिए अनुमति देने के लिए, वर्तमान में उपयोगकर्ता के प्रवेश करने के लिए

मेरे समस्या यह है कि, हालांकि मेरा मानना ​​है कि मैं पत्र के लिए प्रलेखन का पालन किया है, मैं अभी भी एक null नियंत्रक में $request->getUser() के लिए हो रही है है।

नोट: मैं त्रुटि कोड लघु

ApiKeyAuthenticator.php

बात यह है कि अनुरोध इसे से API कुंजी हड़पने के लिए के हिस्से को संसाधित करता है रखने के लिए जाँच हटा दिया है । यह एक हेडर या कुछ भी हो सकता है, लेकिन मैं GET से apiKey के साथ चिपक रहा हूं। दस्तावेज़ से

मतभेद, काफी 0 अलग है कि मैं उपयोगकर्ता निम्न दस्तावेज़ों की this part सत्र में प्रमाणीकृत रखने के लिए कोशिश कर रहा हूँ से। मैं डिफ़ॉल्ट डीबी कार्यान्वयन के साथ चिपके हुए कर रहा हूँ -

class ApiKeyAuthenticator implements SimplePreAuthenticatorInterface 
{ 
    public function createToken(Request $request, $providerKey) 
    { 
     $apiKey = $request->query->get('apiKey'); 

     return new PreAuthenticatedToken(
      'anon.', 
      $apiKey, 
      $providerKey 
     ); 
    } 

    public function authenticateToken(TokenInterface $token, UserProviderInterface $userProvider, $providerKey) 
    { 
     $apiKey = $token->getCredentials(); 
     $username = $userProvider->getUsernameForApiKey($apiKey); 

     // The part where we try and keep the user in the session! 
     $user = $token->getUser(); 
     if ($user instanceof ApiKeyUser) { 
      return new PreAuthenticatedToken(
       $user, 
       $apiKey, 
       $providerKey, 
       $user->getRoles() 
      ); 
     } 


     $user = $userProvider->loadUserByUsername($username); 

     return new PreAuthenticatedToken(
      $user, 
      $apiKey, 
      $providerKey, 
      $user->getRoles() 
     ); 
    } 

    public function supportsToken(TokenInterface $token, $providerKey) 
    { 
     return $token instanceof PreAuthenticatedToken && $token->getProviderKey() === $providerKey; 
    } 
} 

ApiKeyUserProvider.php

कस्टम उपयोगकर्ता प्रदाता कहीं से लोड किया जा सकता से एक उपयोगकर्ता वस्तु लोड करने के लिए।

अंतर: केवल तथ्य यह है मैं, निर्माता में भंडार सुई डीबी के लिए कॉल करने के लिए है कि के रूप में किए गए दस्तावेज़ों का उल्लेख करने लेकिन नहीं दिखा, और भी refreshUser() में $user लौटने।

class ApiKeyUserProvider implements UserProviderInterface 
{ 
    protected $repo; 

    // I'm injecting the Repo here (docs don't help with this) 
    public function __construct(UserRepository $repo) 
    { 
     $this->repo = $repo; 
    } 

    public function getUsernameForApiKey($apiKey) 
    { 
     $data = $this->repo->findUsernameByApiKey($apiKey); 

     $username = (!is_null($data)) ? $data->getUsername() : null; 

     return $username; 
    } 

    public function loadUserByUsername($username) 
    { 
     return $this->repo->findOneBy(['username' => $username]); 
    } 

    public function refreshUser(UserInterface $user) 
    { 
     // docs state to return here if we don't want stateless 
     return $user; 
    } 

    public function supportsClass($class) 
    { 
     return 'Symfony\Component\Security\Core\User\User' === $class; 
    } 
} 

ApiKeyUser.php

यह मेरा कस्टम उपयोगकर्ता वस्तु है।

मेरे यहां एकमात्र अंतर यह है कि इसमें सिद्धांत एनोटेशन (आपकी सैनिटी के लिए हटाया गया) और टोकन के लिए एक कस्टम फ़ील्ड शामिल है। इसके अलावा, मैंने \Serializable हटा दिया क्योंकि ऐसा कुछ नहीं लगता था और स्पष्ट रूप से सिम्फनी को उपयोगकर्ता को फिर से बनाने के लिए $id मान की आवश्यकता होती है।

class ApiKeyUser implements UserInterface 
{ 
    private $id; 
    private $username; 
    private $password; 
    private $email; 
    private $salt; 
    private $apiKey; 
    private $isActive; 

    public function __construct($username, $password, $salt, $apiKey, $isActive = true) 
    { 
     $this->username = $username; 
     $this->password = $password; 
     $this->salt = $salt; 
     $this->apiKey = $apiKey; 
     $this->isActive = $isActive; 
    } 

    //-- SNIP getters --// 
} 

security.yml

# Here is my custom user provider class from above 
providers: 
    api_key_user_provider: 
     id: api_key_user_provider 

firewalls: 
    # Authentication disabled for dev (default settings) 
    dev: 
     pattern: ^/(_(profiler|wdt)|css|images|js)/ 
     security: false 
    # My new settings, with stateless set to false 
    secured_area: 
     pattern: ^/ 
     stateless: false 
     simple_preauth: 
      authenticator: apikey_authenticator 
     provider: 
      api_key_user_provider 

services.yml

जाहिर है मैं प्रदाता में भंडार इंजेक्षन करने में सक्षम होने की जरूरत है।

api_key_user_repository: 
    class: Doctrine\ORM\EntityRepository 
    factory: ["@doctrine.orm.entity_manager", getRepository] 
    arguments: [AppBundle\Security\ApiKeyUser] 

api_key_user_provider: 
    class: AppBundle\Security\ApiKeyUserProvider 
    factory_service: doctrine.orm.default_entity_manager 
    factory_method: getRepository 
    arguments: ["@api_key_user_repository"] 

apikey_authenticator: 
    class: AppBundle\Security\ApiKeyAuthenticator 
    public: false 

डिबगिंग। यह ध्यान रखना दिलचस्प है कि, ApiKeyAuthenticator.php में, $user = $token->getUser(); पर authenticateToken() पर कॉल हमेशा anon. उपयोगकर्ता दिखाता है, इसलिए यह स्पष्ट रूप से सत्र में संग्रहीत नहीं किया जा रहा है।

Debug 1 Debug 2

यह भी ध्यान रखें कि कैसे प्रमाणक के निचले भाग में हम वास्तव में एक उपयोगकर्ता डेटाबेस से पाया के साथ एक नया PreAuthenticatedToken वापसी करते हैं:

Debug 3 Debug 4

तो यह स्पष्ट रूप से पाया मैं और यह लौट रहा हूं कि इसे यहां क्या माना जाता है, लेकिन नियंत्रक में उपयोगकर्ता कॉल null देता है। मैं क्या गलत कर रहा हूं? क्या यह मेरे कस्टम उपयोगकर्ता या कुछ के कारण सत्र में क्रमबद्ध करने में विफलता है? मैंने दस्तावेज में कहीं भी सभी उपयोगकर्ता गुणों को सार्वजनिक रूप से सेट करने का प्रयास किया लेकिन इससे कोई फर्क नहीं पड़ता।

+0

आप इस परीक्षण करने की कोशिश कर सकते हैं, अपने फ़ायरवॉल में जोड़ें: 'access_control: - {पथ:^/, भूमिकाओं: IS_AUTHENTICATED_ANONYMOUSLY}' – COil

+0

मैं इस 'security.yml में' security' के तहत एक कुंजी के रूप में जोड़ा '। कोई परिवर्तन नहीं होता है। – Jimbo

+0

क्या आप अपनी सभी संपत्तियों को ApiKeyUser क्लास में संरक्षित के रूप में सेट करके आजमा सकते हैं? – hasumedic

उत्तर

2

तो यह पता चला है कि नियंत्रक में $request->getUser() पर कॉल करना वास्तव में वर्तमान में प्रमाणीकृत उपयोगकर्ता को वापस नहीं करता है जैसा कि मैंने अपेक्षा की थी। यह इस ऑब्जेक्ट एपीआई imho के लिए सबसे अधिक समझ जाएगा।

आप वास्तव में Request::getUser() के लिए कोड को देखें, तो यह इस तरह दिखता है:

/** 
* Returns the user. 
* 
* @return string|null 
*/ 
public function getUser() 
{ 
    return $this->headers->get('PHP_AUTH_USER'); 
} 

HTTP मूल प्रमाणीकरण के लिए है यही कारण है कि! आदेश प्राप्त करने के लिए वर्तमान में उपयोगकर्ता के प्रवेश में, आप इस हर बार क्या करने की जरूरत:

$this->get('security.token_storage')->getToken()->getUser(); 

यह है, वास्तव में, मुझे वर्तमान में उपयोगकर्ता के प्रवेश देते हैं। उम्मीद है कि ऊपर दिया गया सवाल दिखाता है कि एपीआई टोकन द्वारा सफलतापूर्वक प्रमाणीकरण कैसे करें।

वैकल्पिक रूप से, $this->get() पर कॉल न करें क्योंकि यह एक सेवा लोकेटर है। अपने आप को नियंत्रक से हटा दें और टोकन सेवा को इंजेक्ट करने के बजाय टोकन सेवा को इंजेक्ट करें।

0

अपने नियंत्रक के अंदर प्राप्त करने के लिए वर्तमान में उपयोगकर्ता में लॉग इन बस फोन:

$this->getUser(); 

यह Symfony के ControllerTrait, जो मूल रूप से Jimbo के जवाब में प्रदान की कोड लपेटता में एक विधि के पास भेजेगा।

protected function getUser() 
{ 
    if (!$this->container->has('security.token_storage')) { 
     throw new \LogicException('The SecurityBundle is not registered in your application. Try running "composer require symfony/security-bundle".'); 
    } 

    if (null === $token = $this->container->get('security.token_storage')->getToken()) { 
     return; 
    } 

    if (!is_object($user = $token->getUser())) { 
     // e.g. anonymous authentication 
     return; 
    } 

    return $user; 
} 
+0

हालांकि यह कुछ के लिए सहायक है (और आपके उत्तर मार्को के लिए धन्यवाद!), कुछ ऐसी चीजें हैं जिन्हें उल्लेख करने की आवश्यकता है। '$ this-> कंटेनर 'एक सेवा लोकेटर (विरोधी पैटर्न) है और हमें इसके बजाय निर्भरता इंजेक्शन का उपयोग करना चाहिए। इसके अलावा, सामान्य रूप से लक्षण और सिम्फनी के नियंत्रक वर्ग को विस्तारित करके सिम्फनी के साथ मिलकर कई (युग्मन) द्वारा खराब प्रथाओं को भी माना जाता है। उम्मीद है की वो मदद करदे! – Jimbo

संबंधित मुद्दे