[英]Could not denormalize object of type, no supporting normalizer found. Symfony 2.8
[英]Symfony authentication with guard always return “Username could not be found.”
我知道有很多關於這個主題的線索,但沒有一個對我有幫助......
我正在開發一個應用程序,您必須在該應用程序的每個請求的標頭中發送訪問令牌。 我使用 Guard 管理這種安全性。
對於我的測試,當我發送一個錯誤的令牌時,或者當我不發送它時,必須適當地調用 start() 或 onAuthenticationFailure() 方法。 但它不起作用。 我每次都有同樣的錯誤。 看起來這些方法從未被調用過。
未發送授權
GET /BileMo/web/app_dev.php/api/products/2 HTTP/1.1
Host: localhost:8888
Content-Type: application/json
{
"message": "Username could not be found."
}
無效的訪問令牌
GET /BileMo/web/app_dev.php/api/products/2 HTTP/1.1
Host: localhost:8888
Content-Type: application/json
Authorization: *Fake Facebook Token*
{
"message": "Username could not be found."
}
而不是:
{
"message": "Authorization required"
}
要么
{
"message": "The facebook access token is wrong!"
}
使用正確的訪問令牌,請求將正確返回給用戶。
請求示例:
GET /BileMo/web/app_dev.php/api/products/2 HTTP/1.1
Host: localhost:8888
Content-Type: application/json
Authorization: *Facebook Token*
以下是我的代碼的重要部分:
安全.yml
security:
encoders:
FOS\UserBundle\Model\UserInterface: sha512
role_hierarchy:
ROLE_ADMIN: ROLE_USER
ROLE_SUPER_ADMIN: ROLE_USER
providers:
fos_userbundle:
id: fos_user.user_provider.username_email
api_key_user_provider:
entity:
class: FacebookTokenBundle:User
property: facebook_access_token
firewalls:
api:
pattern: ^/api
stateless: true
anonymous: false
guard:
authenticators:
- AppBundle\Security\FacebookAuthenticator
dev:
pattern: ^/(_(profiler|wdt)|css|images|js)/
security: false
main:
pattern: ^/
form_login:
provider: fos_userbundle
csrf_token_generator: security.csrf.token_manager
login_path: /login
check_path: /login_check
oauth:
resource_owners:
facebook: "/login/check-facebook"
login_path: /login
failure_path: /login
oauth_user_provider:
#this is my custom user provider, created from FOSUBUserProvider - will manage the
#automatic user registration on your site, with data from the provider (facebook. google, etc.)
service: my_user_provider
logout: true
anonymous: true
login:
pattern: ^/login$
security: false
remember_me:
secret: "%secret%"
lifetime: 31536000 # 365 days in seconds
path: /
domain: ~ # Defaults to the current domain from $_SERVER
access_control:
- { path: ^/login$, role: IS_AUTHENTICATED_ANONYMOUSLY }
- { path: ^/register, role: IS_AUTHENTICATED_ANONYMOUSLY }
- { path: ^/resetting, role: IS_AUTHENTICATED_ANONYMOUSLY }
- { path: ^/admin/, role: ROLE_ADMIN }
- { path: ^/api, role: ROLE_USER }
FacebookAuthenticator.php
namespace AppBundle\Security;
use Symfony\Component\HttpFoundation\Request;
use Symfony\Component\HttpFoundation\JsonResponse;
use Symfony\Component\HttpFoundation\Response;
use Symfony\Component\Security\Core\User\UserInterface;
use Symfony\Component\Security\Guard\AbstractGuardAuthenticator;
use Symfony\Component\Security\Core\Authentication\Token\TokenInterface;
use Symfony\Component\Security\Core\Exception\AuthenticationException;
use HWI\Bundle\OAuthBundle\OAuth\Response\UserResponseInterface;
use Doctrine\ORM\EntityManager;
use Symfony\Component\Security\Core\User\UserProviderInterface;
class FacebookAuthenticator extends AbstractGuardAuthenticator
{
public function __construct(EntityManager $em)
{
$this->em = $em;
}
/**
* Called when authentication is needed, but it's not sent
*/
public function start(Request $request, AuthenticationException $authException = null)
{
$data = array(
// you might translate this message
'message' => 'Authentication Required'
);
return new JsonResponse($data, Response::HTTP_UNAUTHORIZED);
}
/**
* Called on every request. Return whatever credentials you want to
* be passed to getUser(). Returning null will cause this authenticator
* to be skipped.
*/
public function getCredentials(Request $request)
{
if (!$token = $request->headers->get('Authorization')) {
// No token?
$token = null;
}
// What you return here will be passed to getUser() as $credentials
return array(
'token' => $token,
);
}
public function getUser($credentials, UserProviderInterface $userProvider)
{
$user = $this->em->getRepository('FacebookTokenBundle:User')
->findOneBy(array('facebook_access_token' => $credentials));
return $user;
}
public function checkCredentials($credentials, UserInterface $user)
{
if ($user->getFacebookAccessToken() === $credentials['token']) {
return true;
}
return new JsonResponse(array('message' => 'The facebook access token is wrong!', Response::HTTP_FORBIDDEN));
}
public function onAuthenticationSuccess(Request $request, TokenInterface $token, $providerKey)
{
// on success, let the request continue
return null;
}
public function onAuthenticationFailure(Request $request, AuthenticationException $exception)
{
$data = array(
'message' => strtr($exception->getMessageKey(), $exception->getMessageData())
// or to translate this message
// $this->translator->trans($exception->getMessageKey(), $exception->getMessageData())
);
return new JsonResponse($data, Response::HTTP_FORBIDDEN);
}
public function supportsRememberMe()
{
return false;
}
}
這種行為是意料之中的。 AbstractGuardAuthenticator 的接口過於通用,如果需要,您需要根據需要對其進行定制。
例如,要出現錯誤“需要授權” - 您可以在 getCredentials() 方法中拋出 AuthenticationException 。 異常將在 symfony 核心中被捕獲,並且方法 start() 將最終被調用。
public function getCredentials(Request $request)
{
if (!$request->headers->has('Authorization')) {
throw new AuthenticationException();
}
...
}
方法 onAuthenticationFailure() 通常用於在憑據錯誤的情況下將用戶重定向到登錄頁面。 如果標題中有 API 密鑰,則不需要此功能。 同樣在當前實現中如何分離,何時“API 密鑰不正確”和“未找到用戶”?
上面的答案有點正確,但有一些更正:
認證(后衛)內拋出的任何異常本身會引發onAuthenticationFailure()
public function onAuthenticationFailure(Request $request, AuthenticationException $exception): JsonResponse
{
return new JsonResponse(['message' => 'Forbidden'], Response::HTTP_FORBIDDEN);
}
public function start(Request $request, AuthenticationException $authException = null): JsonResponse
{
return new JsonResponse(['message' => 'Authentication Required'], Response::HTTP_UNAUTHORIZED);
}
例如,當您從應用程序中(例如在控制器中)拋出AccessDeniedException
時,將調用方法start()
。 也許一個很好的用例是,比如說,你想將一個特定用戶列入一條特定路線的黑名單,並且你不想用不必要的膨脹來填充你的保護驗證器。
/**
* @Route("/something-special")
*/
public function somethingSpecial()
{
if ($this->getUser()->getUsername() === 'a_banned_user') {
throw new AccessDeniedException();
}
// ...
}
然后使用以下命令進行測試:
$ curl -H "Auth-Header-Name: the_auth_token" http://site.local/something-special
{"message":"Authentication Required"}
但是,另一方面,如果由於缺少令牌標頭而引發異常,則onAuthenticationFailure()
將改為運行:
public function getCredentials(Request $request): array
{
if (!$request->headers->has('Authorization')) {
throw new AuthenticationException('Authentication header missing.');
}
// ...
}
然后用(注意: AuthenticationException 消息在 onAuthenticationFailure() 中被忽略,只返回一個通用的“禁止”消息,如上所示):
$ curl http://site.local/something-special
{"message":"Forbidden"}
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.