2017-03-03 87 views
0

我是symfony的新手,我试图获得一个基于JWT的基本后卫身份验证器。这项工作主要来自这里的文章,我已经删除了任何用户检查(现在):http://kolabdigital.com/lab-time/symfony-json-web-tokens-authentication-guardSymfony后卫:access_control没有任何效果

我觉得有些东西我不明白,因为我无法使它工作。更确切地说,即使在我实施的例外情况下,它在任何地方都可以工作。

这里是检查服务,基本相同的文章,没有用户管理,并且以位的日志记录:

<?php 

namespace AppBundle\Security; 

use Lexik\Bundle\JWTAuthenticationBundle\Encoder\DefaultEncoder; 
use Lexik\Bundle\JWTAuthenticationBundle\TokenExtractor\AuthorizationHeaderTokenExtractor; 
use Symfony\Component\HttpFoundation\JsonResponse; 
use Symfony\Component\HttpFoundation\Request; 
use Symfony\Component\HttpFoundation\Response; 
use Symfony\Component\Security\Core\Authentication\Token\TokenInterface; 
use Symfony\Component\Security\Core\Exception\AuthenticationException; 
use Symfony\Component\Security\Core\User\UserInterface; 
use Symfony\Component\Security\Core\User\UserProviderInterface; 
use Symfony\Component\Security\Guard\AbstractGuardAuthenticator; 
use Psr\Log\LoggerInterface; 

class TokenAuthenticator extends AbstractGuardAuthenticator 
{ 
    private $jwtEncoder; 
    private $logger; 

    public function __construct(DefaultEncoder $jwtEncoder, LoggerInterface $logger) 
    { 
     $this->logger = $logger; 
     $this->jwtEncoder = $jwtEncoder; 
    } 

    public function start(Request $request, AuthenticationException $authException = null) 
    { 
     $route = $request->attributes->get('_route'); 
     $url = $request->getUri(); 
     $this->logger->info($route . ' : ' . $url); 
     return new JsonResponse('Authentication required', 401); 
    } 

    public function getCredentials(Request $request) 
    { 

     if(!$request->headers->has('Authorization')) { 
      return; 
     } 

     $extractor = new AuthorizationHeaderTokenExtractor(
      'Bearer', 
      'Authorization' 
     ); 

     $token = $extractor->extract($request); 

     if(!$token) { 
      return; 
     } 

     return $token; 
    } 

    public function getUser($credentials, UserProviderInterface $userProvider) 
    { 
     $data = $this->jwtEncoder->decode($credentials); 

     if(!$data){ 
      return; 
     } 

     $username = $data['username']; 

     // TODO get user from user collection 
     $user = ['username' => $username]; 

     // Is user is encoded in token and exists, then it's fine 
     if(!$user){ 
      return; 
     } 

     return $user; 
    } 


    public function checkCredentials($credentials, UserInterface $user) 
    { 
     return true; 
    } 

    public function onAuthenticationFailure(Request $request, AuthenticationException $exception) 
    { 
     return new JsonResponse([ 
      'message' => $exception->getMessage() 
     ], 401); 
    } 

    public function onAuthenticationSuccess(Request $request, TokenInterface $token, $providerKey) 
    { 
     return; 
    } 

    public function supportsRememberMe() 
    { 
     return false; 
    } 

} 

而且security.yml,一切排除在外,只检查行为。

# To get started with security, check out the documentation: 
# http://symfony.com/doc/current/security.html 
security: 

    # http://symfony.com/doc/current/security.html#b-configuring-how-users-are-loaded 
    providers: 
     in_memory: 
      memory: ~ 

    firewalls: 
     # disables authentication for assets and the profiler, adapt it according to your needs 
     dev: 
      pattern: ^/(_(profiler|wdt)|css|images|js)/ 
      security: false 

################################# 
# Secured section 
# 

     # Custom authentication firewall for all request thats starts from /api 
     api: 
      pattern: ^/api 
      guard: 
       authenticators: 
        - app.token_authenticator 


################################# 
# Main Configuration 
# 

     main: 
      anonymous: ~ 
      # activate different ways to authenticate 

      # http_basic: ~ 
      # http://symfony.com/doc/current/security.html#a-configuring-how-your-users-will-authenticate 

      # form_login: ~ 
      # http://symfony.com/doc/current/cookbook/security/form_login_setup.html 



    access_control: 
     #- { path: ^/auth, roles: IS_AUTHENTICATED_ANONYMOUSLY } 
     #- { path: ^/version, roles: IS_AUTHENTICATED_ANONYMOUSLY } 
     #- { path: ^/api, roles: [ROLE_USER, ROLE_API_USER] } 
     - { path: ^/api, roles: IS_AUTHENTICATED_ANONYMOUSLY } 

     #- { path: ^/(css|js), roles: IS_AUTHENTICATED_ANONYMOUSLY } 
     #- { path: ^/(_wdt|_profiler), roles: IS_AUTHENTICATED_ANONYMOUSLY } 
     #- { path: ^/, roles: ROLE_USER } 

我只是把后卫上^/API的地方,放一个control_access在同一路径上允许匿名。我期望防护服务不会在具有该配置的任何路径上调用,但每次都会调用它。我想我错过了一些关于它如何工作的理解。 我的理解是:

  • 访问控制是任何事情之前
  • 如果有一个匹配的行,它需要它(第一个)
  • 如果IS_AUTHENTICATED_ANONYMOUSLY设置检查,那么防火墙不检查
  • 否则,下一个检查是防火墙的配置,其中它告诉检查与TokenAuthenticator

最初的目的是锁定/api,除了/api/auth/api /版本,可以无需控制地访问。

感谢您的帮助,我认为经过1天半的时间,我无法直截了当地思考。

回答

0

为了记录,我设法解决了这个问题。

首先,Guard Authenticator建立在真正的User Repository上,这不是我们想要的。我们需要使用Redis和Mongo中的UserRepository进行快速检查。另外,我们不需要PHP会话,我们想要一个无状态系统(只有活动令牌在redis中)。

所以我做的是为Guard Authenticator创建一个虚拟的User对象,实现所需的接口。

在访问时,我们通过在redis中获取其令牌以及其他数据来检查用户是否已知。这些附加数据包括所需的用户对象。

在连接上,我们实际上在数据库中检查用户,如果没问题,我们创建一个虚拟用户对象,并用令牌将其推入到redis中。

有了这个系统,一切都很好。这不是最漂亮的解决方案,但它允许在可能有多个实例的无状态环境中使用Guard。