简体   繁体   中英

Symfony2 FOSUserBundle not using UserManager to provide cached User Entity

I am currently trying to build a (hopefully fast) Website in Symfony2. I am using the FOSUserBundle to manage Users easily. I wrote a custom CacheManager to store Entities in Redis and to fetch back valid Entities that are managed by Doctrine. My custom UserManager uses this Service to provide Users Data faster. That all works quiet well, but in fact the UserManager seems to be ignored. Every Request results in a DB query.

My config for the FOSUserBundle (sry. had to replace the Project Name by 'XXX'):

fos_user:
db_driver: orm
firewall_name: main
user_class: XXX\MainBundle\Entity\User
registration:
    form:
        type: XXX_user_registration
        validation_groups: [XXXRegistration]
    confirmation:
        enabled:    true
        template: XXXMainBundle:E-Mail:registration.email.html.twig
        from_email:
            address:        registrierung@XXX.XXX
            sender_name:    XXX Registrierung
profile:
    form:
        type: XXX_user_profile
        validation_groups: [XXXProfile]
resetting:
    email:
        template: XXXMainBundle:E-Mail:resetting.email.html.twig
            from_email:
                address:        reset@XXX.XXX
                sender_name:    XXX.XXX
service:
    mailer: fos_user.mailer.twig_swift
    user_manager: XXX_main_bundle.security.user_manager

My service config:

    XXX_main_bundle.security.user_manager:
    class: XXX\Bundle\MainBundle\Security\UserManager
    arguments: [@XXX_main_bundle.cache_manager, @fos_user.entity_manager, @fos_user.util.canonicalizer.default, @security.password_encoder]

My security config:

security:
providers:
    fos_userbundle:
        id: fos_user.user_provider.username_email

encoders:
    FOS\UserBundle\Model\UserInterface: sha512

role_hierarchy:
    ROLE_ADMIN:       ROLE_USER
    ROLE_SUPER_ADMIN: ROLE_ADMIN
    ROLE_DEVELOPER:   ROLE_SUPER_ADMIN

firewalls:
    main:
        pattern: .*
        form_login:
            provider:   fos_userbundle
            check_path: /login_check
            login_path: /login
        logout:
            path: /logout
        anonymous:    true
    dev:
        pattern: ^/(_(profiler|wdt)|css|images|js)/
        security: false
    login:
        pattern: ^/login$
        security: false

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 }

I can't show you my UserManager and my CacheManager, but if call and use them manually they are working fine! I also tried to write a custom UserProvider with dependency on the UserManager. But that always results in the Error:

ServiceNotFoundException in CheckExceptionOnInvalidReferenceBehaviorPass.php line 59: The service "security.authentication.manager" has a dependency on a non-existent service "security.user.provider.concrete.fos_userbundle".

If I enable both UserProviders the Error disappears but the UserManager ist still ignored. It seems to be used if i am using the built-in Commands of the FOSUserBundle but not when Symfony2 loads the User from the current Session.

Sry. for my bad English and maybe dumb Question but I am open for any criticism.

thx, Justus Klein

EDIT 1: Seems like my UserManager isn`t completely ignored. It returns the User from the Cache but Symfony2 still triggers a DB Query to fetch the User.

EDIT 2: Found out that the refresh function of the UserProvider from the FOSUserBundle loads the User itself from the DB and not through the UserManager (wtf?). So overriding the UserProvider seems to make the clue. But I still get the following Error if I replace it:

ServiceNotFoundException in CheckExceptionOnInvalidReferenceBehaviorPass.php line 59: The service "security.authentication.manager" has a dependency on a non-existent service "security.user.provider.concrete.fos_userbundle".

EDIT 3: I was able to override the UserProvider by giving it the same key as the one from the FOS Bundle:

security:
providers:
    fos_userbundle:
        id: xxx_main_bundle.security.user_provider

That can´t be best Practice ^^ No everything works fine. I think thats the best & fastest way to store the User.

The best solution would be to enable the Second Level Cache feature available in Doctrine ORM 2.5, which performs exactly that: it caches entities in Redis (or any other cache you configure) to retrieve them much faster.

This will not help much when FOSUserBundle loads the user by username (because the ORM uses the id as identifier in its SLC, not the username), but this happens only when submitting the login form, or when reading a remember-me cookie if you use this feature. However, it will work for refresh where the user is loaded by primary key (and also in every other place of your project needing to load a user by primary key). Doctrine will also take care of updating the cache when the user is updated so you get valid data (be sure that you don't edit the user without using the ORM though).

And if you want to optimize the loading of user by username, you could write your own UserProvider which could keep a mapping between usernames and primary keys in Redis and then ask Doctrine for the User object by primary key (which would read it from the ORM cache then), while keeping a refreshing by primary as done in FOSUserBundle own provider (this is done for security reasons). This service can then either use the UserManager or Doctrine directly to load the user by username in case it is not yet in your username-to-id cache.
Overwriting the UserManager would not be needed anymore here, as it would not be used anymore in the critical path of the authentication.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM