2013-03-06 23 views
11

Tôi đang cố triển khai tính năng bảo mật trong dự án Symfony 2.1 nơi quản trị viên có thể tạo người dùng bằng mật khẩu ban đầu và sau đó khi người dùng đăng nhập lần đầu tiên trình xử lý thay đổi mật khẩu được kích hoạt tự động.Làm cách nào để buộc thay đổi mật khẩu bằng FOSUserBundle?

Tôi đang gặp sự cố khi ghi đè các lớp FOSUserBundle và tôi nghĩ chắc chắn điều này đã được xây dựng bằng cách nào đó, ít nhất là một phần, mặc dù tôi không thể thấy nó trong tài liệu ở bất kỳ đâu.

Tôi muốn sử dụng cờ credential_expired trong thực thể. Khi quản trị viên tạo người dùng, điều này sẽ được đặt thành 1. Khi người dùng đăng nhập lần đầu tiên, thông tin đăng nhập_expired được chọn và thay vì ném ngoại lệ, mật khẩu thay đổi sẽ được kích hoạt. Tôi đã thực hiện nó đến nay.

ChangePasswordController sau đó sẽ đảm bảo rằng mật khẩu đã thực sự thay đổi (điều này dường như không phải là hành vi mặc định trong FOS) và thông tin đăng nhập được đặt thành 0. Đây là nơi tôi bị kẹt. Có rất nhiều lớp dịch vụ mà tôi không thể làm cho mọi thứ được tùy chỉnh đúng cách.

+0

Bạn có thể cụ thể hơn không? Có rất nhiều cách tiếp cận để giải quyết vấn đề này. Đăng mã của bạn sẽ giúp chúng tôi xác định cách tiếp tục. Dù sao, việc sử dụng nhiều vai trò hơn cờ, có thể là một ý tưởng hay vì bạn có thể quản lý nó bằng tường lửa symfony. Do đó, những người có vai trò CREDENTIAL_EXPIRED, không thể truy cập toàn bộ web và họ bị mắc kẹt dưới dạng buộc họ phải thay đổi mật khẩu của mình. – Manu

+0

Mã sẽ là FOSUserBundle. Vai trò là một ý tưởng tuyệt vời vì nó sẽ không đòi hỏi các lớp mở rộng. Tôi sẽ cho một phát bắn. Cảm ơn. – David

Trả lời

12

Đây là câu trả lời chi tiết. Cảm ơn Manu vì bàn đạp!

Trước tiên, hãy chắc chắn để có được những FOSUserBundle đúng trong file composer.json ("dev-master", KHÔNG "*"):

"friendsofsymfony/user-bundle":"dev-master" 

Sau đây là tất cả chứa trong bó sử dụng của riêng tôi, mở rộng FOSUserBundle theo hướng dẫn trong tài liệu cài đặt.

PortalFlare/Bundle/UserBundle/Resources/config/services.xml:

<container xmlns="http://symfony.com/schema/dic/services" 
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" 
xsi:schemaLocation="http://symfony.com/schema/dic/services http://symfony.com/schema/dic/services/services-1.0.xsd"> 

<parameters> 
    <parameter key="portal_flare_user.forcepasswordchange.class">PortalFlare\Bundle\UserBundle\EventListener\ForcePasswordChange</parameter> 
    <parameter key="portal_flare_user.passwordchangelistener.class">PortalFlare\Bundle\UserBundle\EventListener\PasswordChangeListener</parameter> 
</parameters> 

<services> 
    <service id="portal_flare_user.forcepasswordchange" class="%portal_flare_user.forcepasswordchange.class%"> 
     <argument type="service" id="router" /> 
     <argument type="service" id="security.context" /> 
     <argument type="service" id="session" /> 
     <tag name="kernel.event_listener" event="kernel.request" method="onCheckStatus" priority="1" /> 
    </service> 
    <service id="portal_flare_user.passwordchange" class="%portal_flare_user.passwordchangelistener.class%"> 
     <argument type="service" id="router" /> 
     <argument type="service" id="security.context" /> 
     <argument type="service" id="fos_user.user_manager" /> 
     <tag name="kernel.event_subscriber" /> 
    </service> 
</services> 

</container> 

PortalFlare/Bundle/UserBundle/EventListener/ForcePasswordChange.php:

<?php 

namespace PortalFlare\Bundle\UserBundle\EventListener; 

use Symfony\Component\HttpFoundation\RedirectResponse; 
use Symfony\Component\HttpKernel\Event\GetResponseEvent; 

use Symfony\Component\Security\Core\SecurityContext; 
use Symfony\Bundle\FrameworkBundle\Routing\Router; 
use Symfony\Component\HttpFoundation\Session\Session; 

/** 
* @Service("request.set_messages_count_listener") 
* 
*/ 
class ForcePasswordChange { 

    private $security_context; 
    private $router; 
    private $session; 

    public function __construct(Router $router, SecurityContext $security_context, Session $session) { 
    $this->security_context = $security_context; 
    $this->router   = $router; 
    $this->session   = $session; 

    } 

    public function onCheckStatus(GetResponseEvent $event) { 

    if (($this->security_context->getToken()) && ($this->security_context->isGranted('IS_AUTHENTICATED_FULLY'))) { 

     $route_name = $event->getRequest()->get('_route'); 

     if ($route_name != 'fos_user_change_password') { 

     if ($this->security_context->getToken()->getUser()->hasRole('ROLE_FORCEPASSWORDCHANGE')) { 

      $response = new RedirectResponse($this->router->generate('fos_user_change_password')); 
      $this->session->setFlash('notice', "Your password has expired. Please change it."); 
      $event->setResponse($response); 

     } 

     } 

    } 

    } 

} 

PortalFlare/Bundle/UserBundle/EventListener/PasswordChangeListener.php:

<?php 
namespace PortalFlare\Bundle\UserBundle\EventListener; 

use FOS\UserBundle\FOSUserEvents; 
use FOS\UserBundle\Event\FormEvent; 
use FOS\UserBundle\Doctrine\UserManager; 
use Symfony\Component\EventDispatcher\EventSubscriberInterface; 
use Symfony\Component\HttpFoundation\RedirectResponse; 
use Symfony\Component\Routing\Generator\UrlGeneratorInterface; 
use Symfony\Component\Security\Core\SecurityContext; 

/** 
* Listener responsible to change the redirection at the end of the password change 
*/ 
class PasswordChangeListener implements EventSubscriberInterface { 
    private $security_context; 
    private $router; 
    private $usermanager; 

    public function __construct(UrlGeneratorInterface $router, SecurityContext $security_context, UserManager $usermanager) { 
    $this->security_context = $security_context; 
    $this->router   = $router; 
    $this->usermanager  = $usermanager; 
    } 

    /** 
    * {@inheritDoc} 
    */ 
    public static function getSubscribedEvents() { 
    return array(
     FOSUserEvents::CHANGE_PASSWORD_SUCCESS => 'onChangePasswordSuccess', 
    ); 
    } 

    public function onChangePasswordSuccess(FormEvent $event) { 

    $user = $this->security_context->getToken()->getUser(); 
    $user->removeRole('ROLE_FORCEPASSWORDCHANGE'); 
    $this->usermanager->updateUser($user); 

    $url = $this->router->generate('_welcome'); 
    $event->setResponse(new RedirectResponse($url)); 
    } 
} 

Vấn đề với FOSUserBundle không thực sự đảm bảo rằng người dùng thay đổi mật khẩu là một vấn đề cho một ngày khác.

Tôi hy vọng điều này sẽ giúp ai đó.

4

Cách tiếp cận tốt có thể bắt đầu xác định ROLE_USER sẽ là vai trò có quyền truy cập vào toàn bộ ứng dụng. Khi người dùng đăng ký, tự động thêm anh ta vào ROLE_CREDENTIAL_EXPIRED hoặc một cái gì đó tương tự. Sử dụng JMSSecurityExtraBundle, bạn có thể sử dụng chú thích trong bộ điều khiển của mình, quyết định xem người dùng có vai trò cụ thể có thể truy cập hay không. Kiểm tra các tài liệu về cách Symfony handle the HTTP Authentication.

+0

Poor @Manu xứng đáng là một upvote tôi nghĩ :) –

+0

Đó là loại rất tốt của bạn! Cảm ơn bạn @DarraghEnright :) – Manu

Các vấn đề liên quan