Изменение информации пользователя между аутентификацией Ldap и созданием JWT в проекте Symfony и ApiPlatform.Php

Кемеровские программисты php общаются здесь
Ответить
Anonymous
 Изменение информации пользователя между аутентификацией Ldap и созданием JWT в проекте Symfony и ApiPlatform.

Сообщение Anonymous »

Я пытаюсь реализовать аутентификацию пользователей с помощью LDAP и JWT.
Я использую Symfony 6.4 и ApiPlatform 3.4
Вот порядок действий:
Пользователь вводит свое имя пользователя и пароль по маршруту /login_check.
Учетные данные проверяются на сервере LDAP.
В случае успеха я получаю информацию о пользователе из LDAP.
Прежде чем генерировать токен JWT, я хочу получить соответствующий объект «Пользователь» из базы данных моего приложения и внедрить его информацию в JWT.
Однако, когда я декодирую JWT, роли, связанные с пользователем, не соответствуют ролям, которые я сохранил в своей базе данных для этого пользователя.
Вот мой код

Код: Выделить всё

class CustomLdapAuthenticator extends AbstractAuthenticator
{
private $ldap;
private $userProvider;

public function __construct(
private LdapCheck $ldapCheck,
LdapInterface $ldap,
CustomUserProvider $userProvider

){
$this->ldap = $ldap;
$this->userProvider = $userProvider;
}

public function supports(Request $request): ?bool
{
return $request->getPathInfo() === '/login_check' && $request->isMethod('POST');
}

public function authenticate(Request $request): Passport
{
$data = json_decode($request->getContent(), true);
$username = $data['username'] ?? '';
$password = $data['password'] ?? '';

if (!$username || !$password) {
throw new AuthenticationException('blablabla');
}

try {
$this->ldapCheck->getEntryFromLdap($username, $password);
} catch (LdapException $e) {
throw new AuthenticationException('blablabla');
}

$user = $this->userProvider->loadUserByIdentifier($username);

return new SelfValidatingPassport(new UserBadge($username, function() use ($user) {
return $user;
}));
}

public function onAuthenticationSuccess(Request $request, $token, string $firewallName): ?JsonResponse
{
return null;
}

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

Код: Выделить всё

class LdapCheck
{
public function __construct(
private Ldap $ldap,
private Adapter $ldapAdapter,
// ---Those are the parameters that are bind in the services.yaml file
private string $ldapServiceDn,
private string $ldapServiceUser,
private string $ldapServicePassword,
) {
$this->ldap = new Ldap($this->ldapAdapter);
try {
$this->ldap->bind($ldapServiceUser, $ldapServicePassword);
} catch (ConnectionException $e) {
throw $e;
}
}

/**
* Get an entry from the Ldap.
*/
public function getEntryFromLdap(string $username, string $password): ?Entry
{
$ldap = new Ldap($this->ldapAdapter);
$search = false;
$value = null;

try {
$userDn = implode(',', ['uid='.$username, $this->ldapServiceDn]);
$ldap->bind($userDn, $password);
if ($this->ldapAdapter->getConnection()->isBound()) {
$search = $ldap->query(
'dc=domain,dc=com',
'(&(objectClass=person)(| (uid='.$username.')))'
)->execute()->toArray();
}
} catch (ConnectionException $e) {
return null;
}
if ($search && 1 === count($search)) {
$value = $search[0];
}

return $value;
}
}

Код: Выделить всё

class CustomUserProvider implements UserProviderInterface
{
private $entityManager;

public function __construct(EntityManagerInterface $entityManager)
{
$this->entityManager = $entityManager;
}

/**
* @throws UserNotFoundException if the user is not found
*/
public function loadUserByIdentifier(string $identifier): UserInterface
{
$user = $this->entityManager->getRepository(User::class)->findOneBy(['username' =>  $identifier]);

if (!$user) {
throw new UserNotFoundException('User not found.');
}

return $user;
}

public function refreshUser(UserInterface $user): UserInterface
{
if (!$user instanceof User) {
throw new UnsupportedUserException(sprintf('Invalid user class "%s".', get_class($user)));
}

throw new \Exception('TODO: fill in refreshUser() inside '.__FILE__);
}

public function supportsClass(string $class): bool
{
return User::class === $class || is_subclass_of($class, User::class);
}
}
и файл конфигурации

Код: Выделить всё

security:
# https://symfony.com/doc/current/security.html#registering-the-user-hashing-passwords
password_hashers:
Symfony\Component\Security\Core\User\PasswordAuthenticatedUserInterface: 'auto'
# https://symfony.com/doc/current/security.html#loading-the-user-the-user-provider
providers:
# users_in_memory: { memory: null }
app_user_provider:
entity:
class: App\Entity\User
property: userIdentifier

my_ldap:
ldap:
service: Symfony\Component\Ldap\Ldap
base_dn: '%env(LDAP_BASE_DN)%'
search_dn: '%env(LDAP_SEARCH_DN)%' # AD user (read only)
search_password: '%env(LDAP_SEARCH_PASSWORD)%' # AD password (read only)
uid_key: uid

main:
chain:
providers: ['my_ldap', 'app_user_provider']

firewalls:
dev:
pattern: ^/(_(profiler|wdt)|css|images|js)/
security: false

api:
pattern: ^/
stateless: true
custom_authenticators:
- App\Security\CustomLdapAuthenticator
provider: app_user_provider
json_login_ldap:
provider: main
service: Symfony\Component\Ldap\Ldap
dn_string: '%env(LDAP_DN_STRING)%'
check_path: /login_check
username_path: username
password_path: password
success_handler: lexik_jwt_authentication.handler.authentication_success
failure_handler: lexik_jwt_authentication.handler.authentication_failure
entry_point: jwt
jwt: ~
refresh_jwt:
# check_path: /api/token/refresh
check_path: /token/refresh
provider: main

main:
lazy: true

# Easy way to control access for large sections of your site
# Note: Only the *first* access control that matches will be used
access_control:
- { path: ^/docs, roles: PUBLIC_ACCESS }
- { path: ^/(login|token/refresh), roles: PUBLIC_ACCESS }
- { path: ^/, roles: ROLE_USER }
Service.yaml

Код: Выделить всё

    # default configuration for services in *this* file
_defaults:
autowire: true      # Automatically injects dependencies in your services.
autoconfigure: true # Automatically registers your services as commands, event subscribers, etc.

# makes classes in src/ available to be used as services
# this creates a service per class whose id is the fully-qualified class name
App\:
resource: '../src/'
exclude:
- '../src/DependencyInjection/'
- '../src/Entity/'
- '../src/Kernel.php'

Symfony\Component\Ldap\Ldap:
arguments: ['@Symfony\Component\Ldap\Adapter\ExtLdap\Adapter']
tags: ['ldap']
Symfony\Component\Ldap\Adapter\ExtLdap\Adapter:
arguments:
-   host: '%env(LDAP_HOST)%'
port: '%env(LDAP_PORT)%'
encryption: '%env(LDAP_ENCRYPTION)%'
options:
protocol_version: 3
referrals: false

App\Security\CustomUserProvider:
arguments:
$entityManager: '@doctrine.orm.entity_manager'
tags:
- { name: 'security.user_provider' }

App\Security\CustomLdapAuthenticator:
arguments:
$ldap: '@Symfony\Component\Ldap\Ldap'
$userProvider: '@App\Security\CustomUserProvider'
tags: ['monolog.logger']

App\Service\LdapCheck:
bind:
$ldapServiceDn:  '%env(LDAP_BASE_DN)%'
$ldapServiceUser: '%env(LDAP_SEARCH_DN)%'
$ldapServicePassword: '%env(LDAP_SEARCH_PASSWORD)%'
для информации, если я сброшу $token в функцию onAuthenticationSuccess, вот что я получу

Код: Выделить всё

CustomLdapAuthenticator.php on line 68:
Symfony\Component\Security\Http\Authenticator\Token\PostAuthenticationToken {#849 ▼
-user:
App\Entity
\
User {#810 ▼
-id: 3
-username: "john.doe"
-roles: array:1 [▼
0 => "ADDED_ROLE_IN_BDD_VIA_WORKER_SUBSCRIBER"
]
-isActive: null
-lastLogin: null
-lastRefresh: null
}
-roleNames: array:3 [▼
0 => "ADDED_ROLE_IN_BDD_VIA_WORKER_SUBSCRIBER"
1 => "ROLE_USER"
2 => "ENTITY_HARDCODED_ROLE"
]
-attributes: []
-firewallName: "api"
}
но если я декодирую JWT

Код: Выделить всё

{
"iat": 1733330446,
"exp": 1733330491,
"roles": [],
"username": "john.doe"
}
Попробовав так много разных вещей, признаюсь, что я создал беспорядок в своей голове и, вероятно, в своем коде тоже.
Спасибо за вашу помощь

Подробнее здесь: https://stackoverflow.com/questions/792 ... symfony-ap
Ответить

Быстрый ответ

Изменение регистра текста: 
Смайлики
:) :( :oops: :roll: :wink: :muza: :clever: :sorry: :angel: :read: *x)
Ещё смайлики…
   
К этому ответу прикреплено по крайней мере одно вложение.

Если вы не хотите добавлять вложения, оставьте поля пустыми.

Максимально разрешённый размер вложения: 15 МБ.

Вернуться в «Php»