我是Symfony2的新手,因为我需要实施自定义身份验证,因此需要一周的时间来解决问题。我设法创建了所有内容,即令牌,监听器,提供者,工厂,用户提供者等。我的问题是,在身份验证后,我无法看到正确的令牌。我认为UsernamePasswordProvider(由symfony2提供)仍然在身份验证后运行,而用户提供程序中的refreshUser显示错误的令牌。我试着几乎到处搜索但无法解决这个问题。如果有人知道,需要一些帮助/提示。感谢。
这是错误令牌的图像(http://tinypic.com/r/33bl5l0/5)。这应该是 ADWSUserToken
以下是代码。我正在使用Symfony 2.2.0
ADWSUserToken.php
namespace StudentIntranet\GenericBundle\Security\Authentication\Token;
use Symfony\Component\Security\Core\Authentication\Token\AbstractToken;
class ADWSUserToken extends AbstractToken
{
public $userObjectType = "";
public $password = "";
public function getCredentials()
{
return '';
}
}
ADWSListener.php
namespace StudentIntranet\GenericBundle\Security\Firewall;
use Symfony\Component\HttpFoundation\Response;
use Symfony\Component\HttpKernel\Event\GetResponseEvent;
use Symfony\Component\Security\Http\Firewall\ListenerInterface;
use Symfony\Component\Security\Http\HttpUtils;
use Symfony\Component\Security\Core\Exception\AuthenticationException;
use Symfony\Component\Security\Core\SecurityContextInterface;
use Symfony\Component\Security\Core\Authentication\AuthenticationManagerInterface;
use StudentIntranet\GenericBundle\Security\Authentication\Token\ADWSUserToken;
class ADWSListener implements ListenerInterface
{
protected $securityContext;
protected $authenticationManager;
protected $providerKey;
public function __construct(SecurityContextInterface $securityContext, AuthenticationManagerInterface $authenticationManager)
{
$this->securityContext = $securityContext;
$this->authenticationManager = $authenticationManager;
}
public function handle(GetResponseEvent $event)
{
$httpUtils = new HttpUtils();
$request = $event->getRequest();
$form = $request->request->get('form');
foreach (array('_token', 'username', 'password') as $val) {
$$val = isset($form[$val]) ? $form[$val] : "";
}
$remember_me = isset($form['remember_me']) ? $form['remember_me'] : "Off";
if (!$username || !$password) {
return;
}
$token = new ADWSUserToken();
$token->setUser($username);
$token->password = $password;
try {
$authToken = $this->authenticationManager->authenticate($token);
$this->securityContext->setToken($authToken);
} catch (AuthenticationException $failed) {
$this->securityContext->setToken(null);
$event->setResponse($httpUtils->createRedirectResponse($request, '/login'));
return;
}
// By default deny authorization
// $response = new Response('NO');
// $response->setStatusCode(403);
// $event->setResponse($response);
}
}
ADWSProvider.php
namespace StudentIntranet\GenericBundle\Security\Authentication\Provider;
use Symfony\Component\Security\Core\Authentication\Provider\AuthenticationProviderInterface;
use Symfony\Component\Security\Core\User\UserProviderInterface;
use Symfony\Component\Security\Core\Exception\AuthenticationException;
use Symfony\Component\Security\Core\Exception\NonceExpiredException;
use Symfony\Component\Security\Core\Authentication\Token\TokenInterface;
use StudentIntranet\GenericBundle\Security\Authentication\Token\ADWSUserToken;
class ADWSProvider implements AuthenticationProviderInterface
{
private $userProvider;
public function __construct(UserProviderInterface $userProvider)
{
$this->userProvider = $userProvider;
}
public function authenticate(TokenInterface $token)
{
$username = $token->getUser();
$password = $token->password;
// here I need to load info using web service ...
if ($username == 'admin' && $password == 'testadmin') {
$token->userObjectType = "User-Staff-MSC";
} elseif ($username == 'user' && $password == 'testuser') {
$token->userObjectType = "User-Participant-MSC";
}
$userObjectTypes = explode("-", $token->userObjectType);
$allowedEntites = array('Staff', 'Participant');
$roles = array(
'Participant' => 'ROLE_USER',
'Staff' => 'ROLE_ADMIN'
);
if (count($userObjectTypes) >= 2 && in_array($userObjectTypes[1], $allowedEntites)) {
$tmp = $this->userProvider->loadUserByUsername($token->getUsername());
$role = $roles[$userObjectTypes[1]];
$authToken = new ADWSUserToken(array($role));
$authToken->setUser($username);
$authToken->userObjectType = $token->userObjectType;
$authToken->setAuthenticated(true);
return $authToken;
}
//$user = $this->userProvider->loadUserByUsername($token->getUsername());
throw new AuthenticationException('The ADWS authentication failed.');
}
public function supports(TokenInterface $token)
{
return $token instanceof ADWSUserToken;
}
}
ADWSFactory.php
namespace StudentIntranet\GenericBundle\DependencyInjection\Security\Factory;
use Symfony\Component\DependencyInjection\ContainerBuilder;
use Symfony\Component\DependencyInjection\Reference;
use Symfony\Component\DependencyInjection\DefinitionDecorator;
use Symfony\Component\Config\Definition\Builder\NodeDefinition;
use Symfony\Bundle\SecurityBundle\DependencyInjection\Security\Factory\SecurityFactoryInterface;
class ADWSFactory implements SecurityFactoryInterface
{
public function create(ContainerBuilder $container, $id, $config, $userProvider, $defaultEntryPoint)
{
$providerId = 'security.authentication.provider.adws.'.$id;
$container
->setDefinition($providerId, new DefinitionDecorator('adws.security.authentication.provider'))
->replaceArgument(0, new Reference($userProvider))
;
$listenerId = 'security.authentication.listener.adws.'.$id;
$listener = $container
->setDefinition($listenerId, new DefinitionDecorator('adws.security.authentication.listener'))
;
return array($providerId, $listenerId, $defaultEntryPoint);
}
public function getPosition()
{
return 'pre_auth';
}
public function getKey()
{
return 'adws';
}
public function addConfiguration(NodeDefinition $node)
{
}
}
ADWSUser.php
namespace StudentIntranet\GenericBundle\Security\User;
use Symfony\Component\Security\Core\User\UserInterface;
use Symfony\Component\Security\Core\User\EquatableInterface;
class ADWSUser implements UserInterface, EquatableInterface
{
private $username;
private $password;
private $salt;
private $roles;
public function __construct($username, $password, $salt, array $roles)
{
$this->username = $username;
$this->password = $password;
$this->salt = $salt;
$this->roles = $roles;
}
public function getRoles()
{
return $this->roles;
}
public function getPassword()
{
return $this->password;
}
public function getSalt()
{
return $this->salt;
}
public function getUsername()
{
return $this->username;
}
public function eraseCredentials()
{
}
public function isEqualTo(UserInterface $user)
{
if (!$user instanceof ADWSUser) {
return false;
}
if ($this->password !== $user->getPassword()) {
return false;
}
if ($this->getSalt() !== $user->getSalt()) {
return false;
}
if ($this->username !== $user->getUsername()) {
return false;
}
return true;
}
}
ADWSUserProvider.php
namespace StudentIntranet\GenericBundle\Security\User;
use Symfony\Component\Security\Core\User\UserProviderInterface;
use Symfony\Component\Security\Core\User\UserInterface;
use Symfony\Component\Security\Core\Exception\UsernameNotFoundException;
use Symfony\Component\Security\Core\Exception\UnsupportedUserException;
class ADWSUserProvider implements UserProviderInterface
{
public function loadUserByUsername($username)
{
return new ADWSUser($username, '', '', array('ROLE_USER'));
}
public function refreshUser(UserInterface $user)
{
return $user;
}
public function supportsClass($class)
{
return $class === 'StudentIntranet\GenericBundle\Security\User\ADWSUser';
}
}
StudentIntranetGenericBundle.php
namespace StudentIntranet\GenericBundle;
use StudentIntranet\GenericBundle\DependencyInjection\Security\Factory\ADWSFactory;
use Symfony\Component\HttpKernel\Bundle\Bundle;
use Symfony\Component\DependencyInjection\ContainerBuilder;
class StudentIntranetGenericBundle extends Bundle
{
public function build(ContainerBuilder $container)
{
parent::build($container);
$extension = $container->getExtension('security');
$extension->addSecurityListenerFactory(new ADWSFactory());
}
}
config.yml
imports:
- { resource: parameters.yml }
- { resource: security.yml }
- { resource: "@StudentIntranetGenericBundle/Resources/config/services.yml" }
framework:
#esi: ~
#translator: { fallback: %locale% }
secret: %secret%
router:
resource: "%kernel.root_dir%/config/routing.yml"
strict_requirements: %kernel.debug%
form: ~
csrf_protection: ~
validation: { enable_annotations: true }
templating:
engines: ['twig']
#assets_version: SomeVersionScheme
default_locale: "%locale%"
trusted_proxies: ~
session: ~
fragments: ~
# Twig Configuration
twig:
debug: %kernel.debug%
strict_variables: %kernel.debug%
# Assetic Configuration
assetic:
debug: %kernel.debug%
use_controller: false
bundles: [ ]
#java: /usr/bin/java
filters:
cssrewrite: ~
#closure:
# jar: %kernel.root_dir%/Resources/java/compiler.jar
#yui_css:
# jar: %kernel.root_dir%/Resources/java/yuicompressor-2.4.7.jar
# Doctrine Configuration
doctrine:
dbal:
driver: %database_driver%
host: %database_host%
port: %database_port%
dbname: %database_name%
user: %database_user%
password: %database_password%
charset: UTF8
# if using pdo_sqlite as your database driver, add the path in parameters.yml
# e.g. database_path: %kernel.root_dir%/data/data.db3
# path: %database_path%
orm:
auto_generate_proxy_classes: %kernel.debug%
auto_mapping: true
# Swiftmailer Configuration
swiftmailer:
transport: %mailer_transport%
host: %mailer_host%
username: %mailer_user%
password: %mailer_password%
spool: { type: memory }
主 routing.yml
student_intranet_student:
resource: "@StudentIntranetStudentBundle/Resources/config/routing.yml"
prefix: /student/
student_intranet_admin:
resource: "@StudentIntranetAdminBundle/Resources/config/routing.yml"
prefix: /admin/
student_intranet_generic:
resource: "@StudentIntranetGenericBundle/Resources/config/routing.yml"
prefix: /
student_intranet_empty:
resource: "@StudentIntranetEmptyBundle/Resources/config/routing.yml"
prefix: /empty/
security.yml
jms_security_extra:
secure_all_services: false
expressions: true
security:
encoders:
#Symfony\Component\Security\Core\User\User: plaintext
StudentIntranet\GenericBundle\Security\User\ADWSUser: plaintext
role_hierarchy:
ROLE_ADMIN: [ROLE_USER, ROLE_ALLOWED_TO_SWITCH]
#ROLE_ADMIN: ROLE_USER
#ROLE_SUPER_ADMIN: [ROLE_USER, ROLE_ADMIN, ROLE_ALLOWED_TO_SWITCH]
providers:
#chain_provider:
# chain:
# providers: in_memory #[user_provider, in_memory]
user_provider:
id: adws.user.provider
#in_memory:
# memory:
# users:
# user: { password: userpass, roles: [ 'ROLE_USER' ] }
# admin: { password: adminpass, roles: [ 'ROLE_ADMIN' ] }
# superadmin: { password: superadminpass, roles: [ 'ROLE_SUPER_ADMIN' ] }
firewalls:
dev:
pattern: ^/(_(profiler|wdt)|css|images|js)/
security: false
free_area:
pattern: ^/default/
anonymous: ~
login:
pattern: ^/login$
security: false
secured_area:
pattern: ^/
adws: true
form_login:
provider: user_provider
check_path: login_check
login_path: login
logout:
path: logout
target: /
switch_user: true
access_control:
- { path: ^/login, roles: IS_AUTHENTICATED_ANONYMOUSLY } #, requires_channel: https
- { path: ^/student/, roles: ROLE_USER }
- { path: ^/admin, roles: ROLE_ADMIN }
#- { path: ^/empty, roles: ROLE_SUPER_ADMIN }
GenericBundle routing.yml
root:
path: /
defaults:
_controller: FrameworkBundle:Redirect:urlRedirect
path: /student/
login:
pattern: /login
defaults: { _controller: StudentIntranetGenericBundle:Security:login }
login_check:
pattern: /login_check
#defaults: { _controller: StudentIntranetGenericBundle:Security:loginCheck }
logout:
pattern: /logout
defaults: { _controller: StudentIntranetGenericBundle:Security:logout }
generic_homepage:
pattern: /generic
defaults: { _controller: StudentIntranetGenericBundle:Default:index }
GenericBundle services.yml
parameters:
# student_intranet_generic.example.class: StudentIntranet\GenericBundle\Example
services:
# student_intranet_generic.example:
# class: %student_intranet_generic.example.class%
# arguments: [@service_id, "plain_value", %parameter%]
adws.user.provider:
class: StudentIntranet\GenericBundle\Security\User\ADWSUserProvider
adws.security.authentication.provider:
class: StudentIntranet\GenericBundle\Security\Authentication\Provider\ADWSProvider
arguments: [""]
adws.security.authentication.listener:
class: StudentIntranet\GenericBundle\Security\Firewall\ADWSListener
arguments: ["@security.context", "@security.authentication.manager"]
SecurityController.php
namespace StudentIntranet\GenericBundle\Controller;
use Symfony\Bundle\FrameworkBundle\Controller\Controller;
use Symfony\Component\Security\Core\SecurityContext;
use Symfony\Component\Validator\Constraints\NotBlank;
//use Symfony\Component\HttpFoundation\Request;
class SecurityController extends Controller
{
public function loginAction()
{
$request = $this->getRequest();
$session = $request->getSession();
$authError = SecurityContext::AUTHENTICATION_ERROR;
// get the login error if there is one
if ($request->attributes->has($authError)) {
$error = $request->attributes->get($authError);
} else {
$error = $session->get($authError);
$session->remove($authError);
}
$form = $this->createFormBuilder(array())
->add('username', 'text', array(
'constraints' => new NotBlank(),
))
->add('password', 'password', array(
'constraints' => new NotBlank(),
))
//->add('remember_me', 'checkbox', array(
// 'value' => 'On',
// 'required' => false,
// ))
->getForm();
return $this->render(
'StudentIntranetGenericBundle:Login:index.html.twig',
array(
// last username entered by the user
'last_username' => $session->get(SecurityContext::LAST_USERNAME),
'error' => $error,
'form' => $form->createView()
)
);
}
public function loginCheckAction()
{
// The security layer will intercept this request
/* These are not working...
if ($this->get('security.context')->isGranted('ROLE_SUPER_ADMIN')) {
// ... load admin content here
return $this->redirect($this->generateUrl('student_intranet_empty', array('name' => 'Super Admin')));
} elseif ($this->get('security.context')->isGranted('ROLE_ADMIN')) {
// ... load admin content here
return $this->redirect($this->generateUrl('student_intranet_admin', array('name' => 'Admin')));
} else {
// ... load admin content here
return $this->redirect($this->generateUrl('student_intranet_student', array('name' => 'Student')));
}
*/
}
public function logoutAction()
{
// The security layer will intercept this request
}
}
index.html.twig
{% extends 'StudentIntranetGenericBundle::layout.html.twig' %}
{% block content %}
{% if error %}
<div>{{ error.message }}</div>
{% endif %}
<form action="{{ path('login_check') }}" method="post">
{# this has CSRF protection #}
{{ form_widget(form) }}
<button type="submit">login</button>
</form>
{% endblock %}
答案 0 :(得分:0)
我想我在听众中发现了这个问题。我们需要在成功验证后将事件响应设置为重定向,否则symfony留下的监听器即UsernamePasswordFormAuthenticationListener.php开始运行。另外,我需要正确配置我的用户提供程序。这是为我修改和工作的文件。
P.S。如果需要,我们还需要修改switch用户的监听器。我修改了我的监听器(此处未显示)以处理切换用户,但需要更多配置。
<强> ADWSListener.php 强>
namespace StudentIntranet\GenericBundle\Security\Firewall;
use Symfony\Component\HttpFoundation\Response;
use Symfony\Component\HttpKernel\Event\GetResponseEvent;
use Symfony\Component\Security\Http\Firewall\ListenerInterface;
use Symfony\Component\Security\Http\HttpUtils;
use Symfony\Component\Security\Core\Exception\AuthenticationException;
use Symfony\Component\Security\Core\SecurityContextInterface;
use Symfony\Component\Security\Core\Authentication\AuthenticationManagerInterface;
use StudentIntranet\GenericBundle\Security\Authentication\Token\ADWSUserToken;
class ADWSListener implements ListenerInterface
{
protected $securityContext;
protected $authenticationManager;
protected $providerKey;
public function __construct(SecurityContextInterface $securityContext, AuthenticationManagerInterface $authenticationManager)
{
$this->securityContext = $securityContext;
$this->authenticationManager = $authenticationManager;
}
public function handle(GetResponseEvent $event)
{
$request = $event->getRequest();
$httpUtils = new HttpUtils();
$form = $request->request->get('form');
foreach (array('_token', 'username', 'password') as $val) {
$$val = isset($form[$val]) ? $form[$val] : "";
}
$remember_me = isset($form['remember_me']) ? $form['remember_me'] : "Off";
if (!$username || !$password) {
return;
}
$token = new ADWSUserToken();
$token->setUser($username);
$token->password = $password;
try {
$authToken = $this->authenticationManager->authenticate($token);
$this->securityContext->setToken($authToken);
$event->setResponse($httpUtils->createRedirectResponse($request, '/'));
return;
} catch (AuthenticationException $failed) {
$this->securityContext->setToken(null);
//$event->setResponse($httpUtils->createRedirectResponse($request, '/login'));
return;
}
// By default deny authorization
// $response = new Response('NO');
// $response->setStatusCode(403);
// $event->setResponse($response);
}
}
<强> ADWSUserProvider.php 强>
namespace StudentIntranet\GenericBundle\Security\User;
use Symfony\Component\Security\Core\User\UserProviderInterface;
use Symfony\Component\Security\Core\User\UserInterface;
use Symfony\Component\Security\Core\Exception\UsernameNotFoundException;
use Symfony\Component\Security\Core\Exception\UnsupportedUserException;
class ADWSUserProvider implements UserProviderInterface
{
public function loadUserByUsername($username)
{
// here I need to load info using web service ...
if ($username == 'user') {
return new ADWSUser($username, '', null, array('ROLE_USER'));
} elseif ($username == 'admin') {
return new ADWSUser($username, '', null, array('ROLE_ADMIN'));
}
throw new UsernameNotFoundException(sprintf('Username "%s" does not exist.', $username));
}
public function refreshUser(UserInterface $user)
{
if (!$user instanceof ADWSUser) {
throw new UnsupportedUserException(sprintf('Instances of "%s" are not supported.', get_class($user)));
}
return $this->loadUserByUsername($user->getUsername());
}
public function supportsClass($class)
{
return $class === 'StudentIntranet\GenericBundle\Security\User\ADWSUser';
}
}