Autorização com Zend Framework

PorFernando Geraldo Mantoan em

Veremos aqui uma explicação detalhada dos componentes de Autorização do Zend Framework, demonstrando como restringir acesso a recursos de um sistema, baseando-se nos privilégios que um usuário autenticado possui. Para este artigo, é essencial o estudo do artigo sobre Autenticação com Zend Framework.

Conceitos

Autorização é o ato de verificar as permissões de um usuário já autenticado no sistema e, baseando-se nessas permissões, permitir ou bloquear o acesso desse usuário a determinados recursos da aplicação. Se, por exemplo, em um Sistema de Gestão de Conteúdo (CMS), o usuário logado é um escritor, ele poderia ter acesso à escrita de artigos, porém não poderia ter acesso ao cadastro de novos usuários, pode-se obter esse tipo de funcionalidade por meio de um componente de autorização.

No Zend Framework, esse componente é o Zend_Acl, que fornece a funcionalidade de Lista de Controle de Acesso (ACL) e gestão de privilégios. Em geral, uma aplicação pode usar essa funcionalidade para controlar o acesso a certos objetos protegidos, requeridos por outros objetos.

Existem alguns termos utilizados para melhor explanar as entidades envolvidas no controle de acesso, cada um deles é detalhado a seguir.

Papel (role)

Um papel corresponde a uma responsabilidade de um usuário dentro de um sistema como, por exemplo, o papel de “colunista” ou de “membro”. Isso é encapsulado através da classe Zend_Acl_Role, que é uma classe simples que apenas armazena o nome do papel. Existe também herança de papéis, onde ao se herdar de um papel, é possível fazer tudo que o papel pai faz, além das ações específicas do papel filho. Para se criar um papel e adicioná-lo na lista de controle de acesso, o seguinte código é necessário:

$papelMembro = new Zend_Acl_Role('membro');
$acl = new Zend_Acl();
$acl->addRole($papelMembro);

Para utilizar a herança de papéis, basta passar o nome do papel pai como segundo parâmetro do método addRole(). Para exemplificar isso, o papel “colunista” irá herdar de “membro”, pois um colunista pode fazer tudo o que um membro pode, além de possuir permissões especiais para criar colunas. O código desse exemplo é apresentado abaixo:

$papelMembro = new Zend_Acl_Role('membro');
$papelColunista = new Zend_Acl_Role('colunista');
$acl = new Zend_Acl();
$acl->addRole($papelMembro);
$acl->addRole($papelColunista, 'membro');

Recurso (resource)

Um recurso é algo a ser protegido, o que pode ser um controlador ou uma ação. Um recurso é encapsulado pela classe Zend_Acl_Resource, que simplesmente armazena o nome do recurso que será protegido. Assim como no caso do Zend_Acl_Role, a classe Zend_Acl_Resource oferece suporte à herança, esta sendo definida no segundo parâmetro do método add() de Zend_Acl.

Um exemplo de utilização de recursos é o caso de um sistema de ERP onde usuários do papel “administrador” podem ter acesso ao controlador manutencao, já usuários do papel “operador” podem ter acesso ao controlador produto. Para criar esses recursos, o seguinte código é necessário:

$acl = new Zend_Acl();
$acl->addResource(new Zend_Acl_Resource('manutencao'));
$acl->addResource(new Zend_Acl_Resource('produto'));

Privilégio (privilege)

Um dado recurso pode ter diversas permissões a um determinado papel, essas permissões são, tipicamente, baseadas nas operações que serão executadas. Exemplos dessas operações podem ser ações de um controlador, como, por exemplo: “adicionar” e “visualizar”. Esse tipo de acesso exigido é facilmente configurado com dois métodos do Zend_Acl: allow() e deny(). Um exemplo permitindo e negando ações do controladorartigos ao papel membro é exibido a seguir:

$acl->allow('membro', 'artigos', 'visualizar');
$acl->deny('membro', 'artigos', 'adicionar');

Após permitir ou negar privilégios, é possível verificar se um determinado papel tem acesso a um privilégio com o seguinte código:

if ($acl->isAllowed('member', 'artigos', 'visualizar') {
echo "acesso permitido";
}

Esses são os principais conceitos e métodos relacionados aos componentes de autorização do Zend Framework, agora é hora de implementar um sistema de autorização integrado com o MVC do framework. Existem diversas formas de se implementar essa funcionalidade, a maneira demonstrada neste artigo é apenas uma delas.

Codificando a Autorização

Após uma introdução sobre os componentes de autorização do Zend Framework, é hora de integrar as funcionalidades para aplicar a autorização ao MVC do framework. Antes de mais nada, estou partindo do pressuposto de que o leitor possui todos os fontes do artigo Autenticação com Zend Framework, para reaproveitar o código desenvolvido neste artigo.

Banco de Dados e configurações iniciais

O banco de dados do artigo anterior sofreu algumas modificações, agora foi criada uma tabela para armazenar os perfis (papéis) suportados por essa aplicação. Dois perfis de exemplo são criados, o primeiro corresponde ao perfil de administrador, que pode ter acesso a todos os recursos protegidos, e o segundo ao perfil de escritor, que pode apenas acessar o controlador de notícias. Um usuário para cada perfil também foi criado. O SQL do banco de dados é apresentado abaixo:

CREATE TABLE `perfil`(
id INT UNSIGNED NOT NULL AUTO_INCREMENT PRIMARY KEY,
nome VARCHAR(30)
)ENGINE=InnoDB;
CREATE TABLE `usuario`(
id INT UNSIGNED NOT NULL AUTO_INCREMENT PRIMARY KEY,
login VARCHAR(30) NOT NULL UNIQUE,
senha VARCHAR(60) NOT NULL,
nome_completo VARCHAR(100) NOT NULL,
perfil_id INT UNSIGNED NOT NULL,
FOREIGN KEY(`perfil_id`) REFERENCES `perfil`(`id`)
ON UPDATE CASCADE
ON DELETE CASCADE
)ENGINE=InnoDB;
INSERT INTO `perfil`(nome) VALUES ('admin'), ('writer');
INSERT INTO `usuario`(login, senha, nome_completo, perfil_id) VALUES ('admin', SHA1('admin'), 'Administrador', 1), ('escritor', SHA1('escritor'), 'Escritor', 2);

O arquivo de configurações application/configs/application.ini desenvolvido no artigo anterior não sofrerá alterações impactantes, algumas linhas são adicionadas para configurar as namespaces do autoloader do framework e incluir o plugin de autenticação, que cuidará da parte de verificar o usuário logado e suas permissões, implementado mais adiante. Essas linhas podem ser adicionadas logo acima da seção [staging : production]:

autoloaderNamespaces[] = "Aplicacao"
resources.frontController.plugins.auth = "Aplicacao_Plugin_Auth"

A próxima etapa é adicionar ao Bootstrap a inicialização da classe de ACL que irá popular todas as regras de autorização suportadas pela aplicação. Dentro do arquivo application/Bootstrap.php basta adicionar o seguinte método à classe Bootstrap:

protected function _initAcl()
{
$aclSetup = new Aplicacao_Acl_Setup();
}

No artigo anterior, foram criados os controladores: noticias e auth. Agora, basta criar o controlador usuarios, que será acessível pelo papeladmin, adicionar novas ações a este novo controlador e ao controlador noticias e, por último, adicionar uma nova ação ao controlador error.

zf create controller usuarios
zf create action adicionar noticias
zf create action adicionar usuarios
zf create action forbidden error

A ação forbidden ficará responsável por informar ao usuário que um determinado acesso não foi autorizado pela aplicação. Para exibir essa mensagem ao usuário, basta adicionar o seguinte conteúdo ao arquivo views/scripts/error/forbidden.phtml:

<div style="color: #f00;">Voc&ecirc; n&atilde;o est&aacute; autorizado a ver esta p&aacute;gina</div>

Com isso, a parte inicial da aplicação está pronta, agora é hora de implementar a classe responsável por popular o componente Zend_Acl.

Populando a ACL

A classe Bootstrap irá inicializar uma classe chamada Aplicacao_Acl_Setup, que ficará responsável por popular todos os dados relacionados a papéis, recursos e privilégios do componente Zend_Acl. Essa classe é apresentada abaixo e deve ser gravada no arquivo library/Aplicacao/Acl/Setup.php.

<?php
class Aplicacao_Acl_Setup
{
/**
* @var Zend_Acl
*/
protected $_acl;

public function __construct()
{
$this->_acl = new Zend_Acl();
$this->_initialize();
}

protected function _initialize()
{
$this->_setupRoles();
$this->_setupResources();
$this->_setupPrivileges();
$this->_saveAcl();
}

protected function _setupRoles()
{
$this->_acl->addRole( new Zend_Acl_Role('guest') );
$this->_acl->addRole( new Zend_Acl_Role('writer'), 'guest' );
$this->_acl->addRole( new Zend_Acl_Role('admin'), 'writer' );
}

protected function _setupResources()
{
$this->_acl->addResource( new Zend_Acl_Resource('auth') );
$this->_acl->addResource( new Zend_Acl_Resource('error') );
$this->_acl->addResource( new Zend_Acl_Resource('noticias') );
$this->_acl->addResource( new Zend_Acl_Resource('usuarios') );
}

protected function _setupPrivileges()
{
$this->_acl->allow( 'guest', 'auth', array('index', 'login') )
->allow( 'guest', 'error', array('error', 'forbidden') );
$this->_acl->allow( 'writer', 'noticias', array('index', 'adicionar') )
->allow( 'writer', 'auth', 'logout' );
$this->_acl->allow( 'admin', 'usuarios', array('index', 'adicionar') );
}

protected function _saveAcl()
{
$registry = Zend_Registry::getInstance();
$registry->set('acl', $this->_acl);
}
}

A primeira etapa dessa classe é configurar os papéis. Nesse caso, foram criados os papéis guestwriter e admin. O papel guest foi criado como um facilitador, já que não será utilizado. Pode ocorrer de a aplicação liberar determinados acessos a usuários não-logados, aí que entra esse papel. O papel writer terá os mesmos acessos de guest, além de acessos específicos, assim como o papel admin terá os mesmos acessos que guest e writer, além de outras permissões.

Após ter os papéis definidos, são adicionados os recursos protegidos. Nesse caso, os recursos são os controladores autherrornoticias eusuarios. A próxima etapa é definir os privilégios de um determinado papel a um determinado recurso. O papel guest pode acessar ações de erro e a página de login, já o papel writer pode acessar as ações de guest, as ações do controlador de notícias e a ação de logout do controlador auth. Já o papel admin pode acessar o mesmo que os outros dois papéis, além das ações do controlador de usuários.

Por último, essa ACL é adicionada ao Zend_Registry, para que em outras partes da aplicação ela esteja acessível e devidamente populada.

Verificação de permissões

Tendo a ACL devidamente configurada, agora será possível verificar as permissões do usuário. Para isso, foi criado um plugin que é adicionado ao Zend_Controller_Front no arquivo de configuração definido na primeira etapa das implementações. Esse plugin terá como pai da classe Zend_Controller_Plugin_Abstract, que possui diversos métodos referentes à cada etapa do processo de despacho das classes do Zend Framework. O método utilizado no plugin é o preDispatch() que é chamado antes de uma ação de controlador ser despachada, o que permite reconfigurar a rota caso o usuário não possa ter acesso à página em questão. Esse plugin é exibido abaixo e deve estar gravado em library/Aplicacao/Plugin/Auth.php:

<?php
class Aplicacao_Plugin_Auth extends Zend_Controller_Plugin_Abstract
{
/**
* @var Zend_Auth
*/
protected $_auth = null;
/**
* @var Zend_Acl
*/
protected $_acl = null;
/**
* @var array
*/
protected $_notLoggedRoute = array(
'controller' => 'auth',
'action' => 'login',
'module' => 'default'
);
/**
* @var array
*/
protected $_forbiddenRoute = array(
'controller' => 'error',
'action' => 'forbidden',
'module' => 'default'
);

public function __construct()
{
$this->_auth = Zend_Auth::getInstance();
$this->_acl = Zend_Registry::get('acl');
}

public function preDispatch(Zend_Controller_Request_Abstract $request)
{
$controller = "";
$action = "";
$module = "";
if ( !$this->_auth->hasIdentity() ) {
$controller = $this->_notLoggedRoute['controller'];
$action = $this->_notLoggedRoute['action'];
$module = $this->_notLoggedRoute['module'];
} else if ( !$this->_isAuthorized($request->getControllerName(),
$request->getActionName()) ) {
$controller = $this->_forbiddenRoute['controller'];
$action = $this->_forbiddenRoute['action'];
$module = $this->_forbiddenRoute['module'];
} else {
$controller = $request->getControllerName();
$action = $request->getActionName();
$module = $request->getModuleName();
}
$request->setControllerName($controller);
$request->setActionName($action);
$request->setModuleName($module);
}

protected function _isAuthorized($controller, $action)
{
$this->_acl = Zend_Registry::get('acl');
$user = $this->_auth->getIdentity();
if ( !$this->_acl->has( $controller ) || !$this->_acl->isAllowed( $user, $controller, $action ) )
return false;
return true;
}
}

Primeiramente são definidas as rotas padrão para o caso do usuário não estar logado ou não estar autorizado a ver uma determinada página. Essas rotas são, consecutivamente: auth/login e error/forbidden. No construtor é recuperada a instância atual de Zend_Auth Zend_Acl, ambas usadas no processo de verificação do usuário logado.

A primeira etapa do método preDispatch() é verificar se o usuário está logado e, caso não esteja, os parâmetros da requisição são configurados para a rota de login. A segunda etapa é o caso de o usuário estar logado. Nessa etapa, o objeto que representa o usuário logado é recuperado e é feita uma verificação se esse usuário possui o privilégio correspondente à ação do controlador requisitado. Caso não possua, ou o recurso que representa o controlador não exista, ele é redirecionado para a rota de acesso proibido. Se nenhuma dessas verificações negar o acesso ao usuário, significa que ele está apto a acessar a requisição em questão, portanto os parâmetros dessa requisição são mantidos e o fluxo prossegue.

Models

Tendo todo o processo de verificação pronto, a próxima etapa é criar as classes com regras de negócio da parte de autenticação. Já que, diferente do artigo anterior, este artigo envolve o MVC do Zend Framework, toda a parte de regra de negócio é implementada na camada referente ao Model. A primeira classe criada é a classe que representa um usuário da aplicação. Ela implementará a interface Zend_Acl_Role_Interface, que permite que ela seja tratada como um papel do componente Zend_Acl. Essa classe exige que o método getRoleId() seja implementado e retorne o identificador correspondente ao papel em questão. Essa classe é bem simples e possui apenas métodos getters e setters. Ela deverá estar no arquivo models/Usuario.php.

<?php
class Model_Usuario implements Zend_Acl_Role_Interface
{
private $_userName;
private $_roleId;
private $_fullName;

public function getUserName()
{
return $this->_userName;
}

public function setUserName($userName)
{
$this->_userName = (string) $userName;
}

public function getFullName()
{
return $this->_fullName;
}

public function setFullName($fullName)
{
$this->_fullName = (string) $fullName;
}
/**
*
*/
public function getRoleId()
{
return $this->_roleId;
}

public function setRoleId($roleId)
{
$this->_roleId = (string) $roleId;
}
}

A próxima etapa é implementar a classe que cuidará da regra de negócio de login. Essa classe terá apenas um método estático, responsável por fazer o login do usuário na aplicação. A principal diferença deste método de login com o apresentado no artigo anterior é a modificação da consulta ao banco de dados, para adicionar a tabela perfil aos dados retornados pelo Zend_Auth_Adapter_DbTable. O método join() de Zend_Db_Select adicionará a tabela perfil com o alias “p”, onde a coluna perfil_id da tabela usuario seja igual a coluna id da tabela perfil, retornando a coluna  “nome” da tabela “perfil” com o alias “nome_perfil”. Após validar o login, o objeto retornado pelo adapter é mapeado para um objeto do tipo Model_Usuario e, por último, ele é armazenado à sessão do Zend Framework. Esse model ficará no arquivo models/Auth.php e deve possuir o seguinte conteúdo:

<?php
class Model_Auth
{
public static function login($login, $senha)
{
$dbAdapter = Zend_Db_Table::getDefaultAdapter();
//Inicia o adaptador Zend_Auth para banco de dados
$authAdapter = new Zend_Auth_Adapter_DbTable($dbAdapter);
$authAdapter->setTableName('usuario')
->setIdentityColumn('login')
->setCredentialColumn('senha')
->setCredentialTreatment('SHA1(?)');
//Define os dados para processar o login
$authAdapter->setIdentity($login)
->setCredential($senha);
//Faz inner join dos dados do perfil no SELECT do Auth_Adapter
$select = $authAdapter->getDbSelect();
$select->join( array('p' => 'perfil'), 'p.id = usuario.perfil_id', array('nome_perfil' => 'nome') );
//Efetua o login
$auth = Zend_Auth::getInstance();
$result = $auth->authenticate($authAdapter);
//Verifica se o login foi efetuado com sucesso
if ( $result->isValid() ) {
//Recupera o objeto do usuário, sem a senha
$info = $authAdapter->getResultRowObject(null, 'senha');

$usuario = new Model_Usuario();
$usuario->setFullName( $info->nome_completo );
$usuario->setUserName( $info->login );
$usuario->setRoleId( $info->nome_perfil );

$storage = $auth->getStorage();
$storage->write($usuario);

return true;
}
throw new Exception('Nome de usuário ou senha inválida');
}
}

Controller de Autenticação e demais Views

Agora que toda a regra de negócio foi delegada à camada Model, o controller de autenticação sofre algumas modificações para utilizar essa classe. Nesse caso, ele chamará o método estático Model_Auth::login(), e, caso este lance alguma exceção, irá armazenar a mensagem ao helper FlashMessenger e exibi-la em cima do formulário de login. A classe modificada é apresentada abaixo e, logo em seguida, a view correspondente a essa ação de login é apresentada. O formulário de login é o mesmo implementado no antigo anterior.

<?php
class AuthController extends Zend_Controller_Action
{

public function init()
{
}

public function indexAction()
{
return $this->_helper->redirector('login');
}

public function loginAction()
{
$this->_flashMessenger = $this->_helper->getHelper('FlashMessenger');
$this->view->messages = $this->_flashMessenger->getMessages();
$form = new Form_Login();
$this->view->form = $form;
//Verifica se existem dados de POST
if ( $this->getRequest()->isPost() ) {
$data = $this->getRequest()->getPost();
//Formulário corretamente preenchido?
if ( $form->isValid($data) ) {
$login = $form->getValue('login');
$senha = $form->getValue('senha');

try {
Model_Auth::login($login, $senha);
//Redireciona para o Controller protegido
return $this->_helper->redirector->goToRoute( array('controller' => 'noticias'), null, true);
} catch (Exception $e) {
//Dados inválidos
$this->_helper->FlashMessenger($e->getMessage());
$this->_redirect('/auth/login');
}
} else {
//Formulário preenchido de forma incorreta
$form->populate($data);
}
}
}

public function logoutAction()
{
$auth = Zend_Auth::getInstance();
$auth->clearIdentity();
return $this->_helper->redirector('index');
}
}
<h2>Login</h2>
<?php echo ( sizeof( $this->messages ) > 0 ) ? '<div style="color: #f00;">' . $this->messages[0] . '</div>' : ""; ?>
<?php echo $this->form; ?>

A última etapa é modificar as views dos controladores noticias e usuarios. Essas views são extremamente simples e irão conter apenas uma mensagem de boas-vindas e um link para logout. O arquivo views/scripts/noticias/index.phtml deve conter o seguinte conteúdo:

<h2>Bem-vindo Escritor</h2>
<a href="<?php echo $this->url(array('controller' => 'auth', 'action' => 'logout', 'module' => 'default'), '', true); ?>">
Logout
</a>

E, por último, o arquivo views/scripts/usuarios/index.phtml possui o seguinte conteúdo:

<h2>Bem-vindo Admin</h2>
<a href="<?php echo $this->url(array('controller' => 'auth', 'action' => 'logout', 'module' => 'default'), '', true); ?>">
Logout
</a>

Funcionamento

Após todas as etapas estarem concluídas, a aplicação está devidamente protegida, tanto na questão de permitir apenas usuários logados, como em autorizar aos usuários apenas alguns privilégios baseados em seu perfil. Para o meu caso, a URL da aplicação ficou apontada para:http://localhost/zend_acl, e qualquer tentativa de acessar controllers internos são redirecionadas à pagina de login. Após o login, a aplicação permite ou proíbe o acesso, de acordo com o perfil logado. Abaixo são apresentadas algumas screenshots de cada parte funcional dessa aplicação:

Conclusão

Os componentes do Zend Framework relacionados à autorização apresentam uma API consistente e intuitiva de se utilizar e configurar, o que garante uma baixa curva de aprendizagem para se utilizar as funcionalidades básicas do mesmo. Esses componentes podem ser integrados a todo o processo de despacho da parte MVC do framework através de plugins, o que permite manipular as requisições livremente e, baseado em determinados critérios, redirecionar o usuário para outros pontos da aplicação para informar mensagens de erro ou exigir algum formulário para entrada de dados. O objetivo deste artigo foi demonstrar de forma prática uma maneira de implementar autorização e integrá-la ao MVC, porém, vale ressaltar, que existem diversas formas de se chegar a essa funcionalidade. Para maiores informações, a seção de referências logo abaixo pode ser de grande valia.

No próximo artigo, os componentes de Internacionalização e Localização serão abordados, até lá!

Referências

Deixe um comentário! 47

O seu endereço de e-mail não será publicado. Campos obrigatórios são marcados com *

Comentando como Anônimo

  1. Implementei seu primeiro artigo e deu tudo certo, mas neste segundo está dando um erro, já tentei verificar mas não consigo encontrar, o php me tras este erro:
    Catchable fatal error: Object of class Application_Model_Usuario could not be converted to string in D:\server\testeZend\library\Zend\Acl\Role\Registry.php on line 124

    1. Estranho, está implementando Zend_Acl_Role_Interface? Provavelmente seu Model não implementou a interface e ele tentou utilizar a role como string por isso deu erro. Att.

    2. cara no meu caso foi mais simples, provalvelmente vc esta logadao, e por isso deus os erros, desative o plugin e faça um logout, que ira ocorrer tudo certo

  2. Obrigado por responder, estou meio perdido com o zend ainda, realmente não estava implementando a interface, agora está funcionando mas está indo só para a tela escritor, não redireciona a admin, o que será, outra coisa, o sistema só funciona se colocar “Application_” antes do nome dado, como na classe “Model_Auth” que tem de ser “Application_Model_Auth”, e em vários outros locais do código onde você se refere a um arquivo que se encontra dentro de application, obrigado.

  3. Olá, bom este comportamento de ir para a tela escritor é normal, já que a rota retornada em loginAction está apontando para o controller de notícias. Se quiser apontar para o de usuários aponte em seu browser algum endereço semelhante a: http://localhost/zend_acl/usuarios. Classes com o prefixo “Application_” utilizam a nomenclatura padrão do Zend Framework e este namespace pode ser utilizado para diferenciar suas classes de aplicação das demais classes. Em casos mais simples eu prefiro não utilizar este prefixo e configuro no autoloader, demonstrado no artigo anterior, para utilizar um namespace vazio, isso vai mais de cada um e do tipo de aplicação. Se quiser retirar este prefixo sugiro que veja o artigo de Autenticação e coloque o método _initAutoload em seu Bootstrap. Att.

    1. Entendi, desculpe tantas perguntas, estou tentando compreender como funciona e é o primeiro artigo didático e atual que vejo, como ficaria o teste para redirecionar para os recurso permitidos, por exemplo, admin ir para página permitida a ele e escritor ir para a permitida a ele. Agora quanto ao namespace, mesmo no artigo de autenticação, o php acusou o erro por causa do prefixo “Aplication_”, e eu coloquei exatamente como no modelo apresentado, obrigado.

  4. Bom, você pode verificar qual a role do usuário autenticado utilizando o método getRoleId(), e, no caso de ele ter a role “admin” modificar a rota que ele será direcionado, ficando algo assim:
    $user = Zend_Auth::getInstance()->getIdentity();
    if ($user->getRoleId() == “admin”) {
    return $this->_helper->redirector->goToRoute( array(‘controller’ => ‘usuarios’), null, true);
    } else {
    return $this->_helper->redirector->goToRoute( array(‘controller’ => ‘noticias’), null, true);
    }

  5. Gostaria de colocar este sistema de autenticação e autorização dentro de um módulo admin, onde o index verificaria se o usuário está logado ou não, não estando ele direcionaria para a verificação, caso ele estiver logado mostraria a index.phtml, mas não estou conseguindo configurar e fazer funcionar, estou meio perdido ainda com o zendframework, li vários tutoriais de como usar módulos mas não consegui nada, a estrutura de arquivos seria:

    -library/
    —–Zend/
    -Project/
    -.htaccess
    -index.php
    —–application/
    ————–configs/
    ———————–application.ini
    ————–controllers/
    ————–layouts/
    ————–models/
    ————–modules/
    ———————–Admin/
    ——————————-controllers
    ——————————-forms
    ——————————-models
    ——————————-views
    ————–views/
    ————–Bootstrap.php
    —–libApplication/
    ————–Acl/
    ————–Plugin/
    —–public
    ————–Css/
    ————–JavaScript/
    ————–Images/

    Será que poderia me ajudar a compreender e configurar o sistema, fazer funcionar, obrigado.

    1. Olá, a melhor maneira de te ajudar nisso é mantermos contato via e-mail. Por favor me mande um e-mail com seu Google Talk e podemos conversar melhor. Abraços.

  6. Acho que só faltou uma coisa no tutorial.

    Como tinha definido a Role Guest, na verificação para ver se o usuario está logado deveria definir ele como Guest ao invés de mandar ele para a página de login..

    Mas muito bom o tutorial, parabéns ajudou batante!

  7. otimu!!
    parabens

    mais eu acho pra que esta comecando fica mt confuso. caso queria colocar ele pra fazer toda processo sozinho, teram dificuldades! ( eu acho )

    1. Nesse caso não tem muito o que fazer William, acredito que seria melhor ver alguns artigos básicos de Zend Framework, para partir para esse. Fica a dica para o pessoal.

  8. Concordo com o Fancisco, esse tutorial só serve para desenvolvimento de um sistema em que seria necessário o login para acessoa qualquer parte.
    Fiz uma pequena alteração no código, misturando com o que havia sido desenvolvido no tutorial anterior.

    No arquivo library/Aplicacao/Acl/Auth.php tem um IF que verifica se o usuário está autenticado e se tem autorizacao. Troquei um pouco a ordem das coisas:

    if ( $this->_auth->hasIdentity() ) {
    if ( !$this->_isAuthorized($request->getControllerName(),
    $request->getActionName()) ) {
    $controller = $this->_forbiddenRoute[‘controller’];
    $action = $this->_forbiddenRoute[‘action’];
    $module = $this->_forbiddenRoute[‘module’];
    } else {
    $controller = $request->getControllerName();
    $action = $request->getActionName();
    $module = $request->getModuleName();
    }
    } else {
    $controller = $request->getControllerName();
    $action = $request->getActionName();
    $module = $request->getModuleName();
    }

    Assim, ele primeiro vai testar se o usuário ESTÁ autenticado, e se ele estiver autenticado irá testar a autorização. Assim bloqueamos o acesso do Escritor na área de Admin, porém quem não está logado tem acesso total ao sitema.

    Pra resolver isso vou alterar a função init() de cada controlador que eu quero acesso restrito.
    Então no init() de UsuariosController e NoticiasController eu coloquei o seguinte trecho que estava no exemplo anterior:

    public function init()
    {
    if ( !Zend_Auth::getInstance()->hasIdentity() ) {
    return $this->_helper->redirector->goToRoute( array(‘controller’ => ‘auth’), null, true);
    }
    }

    Essa função vai testar se o usuário está logado. Se não estiver o redirector vai mandar para a página de login independente da permissão.

    Uma última alteração é no application.ini que eu troquei o controlador padrão para o index, assim o site vai sempre para o index ao invés do controlador Auth, como no exemplo.

    resources.frontController.defaultControllerName = “index”

    Na view do index eu coloquei os links para o auth/login, noticias/index e usuarios/index para testar.

    Está pronto!

    Outra coisa legal de se fazer é com a Zend Tool habilitar os layouts:
    ZF ENABLE LAYOUT

    Esse arquivo vai criar a “casca do HTML”, daí lá é possível colocar o doctype, headers, body, etc…

    Não sei se existe uma forma melhor de se fazer isso. Comecei a trabalhar com Zend anteontem e minha formação é em design gráfico. Se tiver, por favor me avise!

    1. Melhorei um pouco a primeira modificação:

      if ( $this->_auth->hasIdentity() && (!$this->_isAuthorized($request->getControllerName(),
      $request->getActionName())) ) {
      $controller = $this->_forbiddenRoute[‘controller’];
      $action = $this->_forbiddenRoute[‘action’];
      $module = $this->_forbiddenRoute[‘module’];

      } else {
      $controller = $request->getControllerName();
      $action = $request->getActionName();
      $module = $request->getModuleName();
      }

  9. Olá, implementei o tutorial e funciono perfeitamente mais como tive que expandir a aplicação usando modulos o acl nao funciona mais. No caso o Application_Plugin_Auth nao é instanciado e qndo eu uso ele pra iniciar no _init do bootstrap nao funciona tb ;/
    alguem sabe como resolve, obrigado

  10. Olá, estou com um pequeno problema, quando início o sistema ele entra em um loop chamando sempre o método preDispatch() na classe Aplicacao_Plugin_Auth, agradeço desde já pela ajuda.

    1. Você poderia implementar um mecanismo de whitelist, poderia ser algo como um array com as páginas públicas, e caso seja uma dessas páginas você não faz as verificações.

  11. Configurei tudo como deveria estar e está apresentando o erro
    Fatal error: Class ‘Aplicacao_Acl_Setup’ not found in C:\wamp\www\zf-auth-tutorial\application\Bootstrap.php on line 17

    Alguem poderia dar um help…

  12. Quando eu retiro a linha do Bootstrap a linha 17 “$aclSetup = new Aplicacao_Acl_Setup();”

    ate funciona mais ai a autenticação fica apenas como Escritor.

    Quando autentico como escritor aparece escritor

    Quando autentico como Admin aparece escritor…

    Acho que ele não está pegando o camito do setup na pasta ACL mais não sei como setar

    fiz todas configurações necessarias de acordo com o tutorial o 1 funcionou blz mais esse ta com esse erro….

    1. Olá, tudo bem? Consulte o código fonte que disponibilizei no GitHub, verifique se com aquela base de código tudo funciona corretamente… Att.

    1. Não, referência da documentação do Zend Framework, conforme citado nas referências externas, sendo essa a mesma fonte do artigo da DevZone.

  13. Fala fernando tranquilo? Conversei com voce a um tempo sobre autorização e você me respondeu no e-mail um tipo de “whitelist” … deu certinho e tal.. mas se eu tenho um controller “x” no modulo admin e um controller “x” no modulo default e liberar “x” no whitelist ele libera o acesso nos 2 módulos :P… então eu segui o exemplo do Gustavo Eidt e funcionou certinho em partes… ele não bloqueia mais as páginas para usuários não logados, porém os privilégios para admin e para writer não se diferenciam mais.. ou seja os 2 tem total acesso… Abraços

    1. Olá Luiz, tudo bem? Mas daí como ficou o código que está permitindo total acesso aos dois? Teria que analisar sucintamente cada pedaço de seu código, se quiser ajuda pode me contactar pelo e-mai. Abraços.

  14. Eu também tive o mesmo problema, mas fiz algumas adaptações e agora está tudo funcionando perfeitamente.

    Tenho uma aplicação modular que funciona para usuários logados ou não logados, neste caso, o usuário assume o role ‘guest’, seguem os dois principais arquivos:

    JR_Plugin_Auth
    ———————–

    ‘admin’,
    ‘controller’=> ‘conta’,
    ‘action’ => ‘login’
    );

    protected $_adminForbiddenRoute = array(
    ‘module’ => ‘admin’,
    ‘controller’=> ‘conta’,
    ‘action’ => ‘proibido’
    );

    protected $_guestNotLoggedRoute = array(
    ‘module’ => ”,
    ‘controller’=> ”,
    ‘action’ => ”
    );

    protected $_guestForbiddenRoute = array(
    ‘module’ => ”,
    ‘controller’=> ”,
    ‘action’ => ”
    );

    public function __construct()
    {
    $this->_auth = Zend_Auth::getInstance();
    $this->_acl = Zend_Registry::get(‘acl’);
    }

    public function preDispatch(Zend_Controller_Request_Abstract $request)
    {
    $module = $request->getModuleName();
    $controller = ”;
    $action = ”;

    if ( !$this->_isAuthorized(
    $module,
    $request->getControllerName(),
    $request->getActionName()
    ))
    {
    // Rotas administrativias
    if($this->_isAdmin($module))
    {
    if( $this->_auth->hasIdentity() )
    {
    $module = $this->_adminForbiddenRoute[‘module’];
    $controller = $this->_adminForbiddenRoute[‘controller’];
    $action = $this->_adminForbiddenRoute[‘action’];
    }
    else
    {
    $module = $this->_adminNotLoggedRoute[‘module’];
    $controller = $this->_adminNotLoggedRoute[‘controller’];
    $action = $this->_adminNotLoggedRoute[‘action’];
    }
    }
    // Rotas de clientes
    else
    {
    if( $this->_auth->hasIdentity() )
    {
    $module = $this->_guestForbiddenRoute[‘module’];
    $controller = $this->_guestForbiddenRoute[‘controller’];
    $action = $this->_guestForbiddenRoute[‘action’];
    }
    else
    {
    $module = $this->_guestNotLoggedRoute[‘module’];
    $controller = $this->_guestNotLoggedRoute[‘controller’];
    $action = $this->_guestNotLoggedRoute[‘action’];
    }
    }
    }
    else
    {
    $controller = $request->getControllerName();
    $action = $request->getActionName();
    }

    $request->setModuleName($module);
    $request->setControllerName($controller);
    $request->setActionName($action);
    }

    protected function _isAuthorized($module, $controller, $action)
    {
    // Se o usuário não estiver logado assume o papel ‘guest’
    $user = $this->_auth->hasIdentity() ? $this->_auth->getIdentity() : ‘guest’;

    if( !$this->_acl->has($module . ‘:’ . $controller)
    || !$this->_acl->isAllowed($user, $module . ‘:’ . $controller, $action)
    )
    {
    return false;
    }
    return true;
    }

    protected function _isAdmin($module)
    {
    if($module == ‘admin’)
    return true;
    return false;
    }
    }

    JR_Acl_Setup
    ——————–
    _acl = new Zend_Acl();
    $this->_initialize();
    }

    protected function _initialize()
    {
    $this->_setupRoles();
    $this->_setupResources();
    $this->_setupPrivileges();
    $this->_saveAcl();
    }

    protected function _setupRoles()
    {
    $this->_acl->addRole( new Zend_Acl_Role(‘guest’) );
    $this->_acl->addRole( new Zend_Acl_Role(‘cliente’), ‘guest’ );
    $this->_acl->addRole( new Zend_Acl_Role(‘admin’) );
    }

    protected function _setupResources()
    {
    $this->_acl->addResource( new Zend_Acl_Resource(‘default:index’) );
    $this->_acl->addResource( new Zend_Acl_Resource(‘produto:index’) );
    $this->_acl->addResource( new Zend_Acl_Resource(‘cliente:conta’) );
    $this->_acl->addResource( new Zend_Acl_Resource(‘admin:conta’) );
    $this->_acl->addResource( new Zend_Acl_Resource(‘admin:produto’) );
    $this->_acl->addResource( new Zend_Acl_Resource(‘admin:categoria’) );
    $this->_acl->addResource( new Zend_Acl_Resource(‘admin:cliente’) );
    }

    protected function _setupPrivileges()
    {
    $this->_acl->allow(‘guest’, ‘default:index’)
    ->allow(‘guest’, ‘cliente:conta’, array(‘login’, ‘registrar’))
    ->allow(‘guest’, ‘admin:conta’, array(‘restrito’, ‘login’));

    $this->_acl->allow(‘cliente’, ‘cliente:conta’);

    $this->_acl->allow(‘admin’, ‘admin:conta’)
    ->allow(‘admin’, ‘admin:produto’)
    ->allow(‘admin’, ‘admin:categoria’)
    ->allow(‘admin’, ‘admin:cliente’);

    $this->_acl->deny(‘cliente’, ‘admin:conta’);
    }

    protected function _saveAcl()
    {
    $registry = Zend_Registry::getInstance();
    $registry->set(‘acl’, $this->_acl);
    }
    }

leia mais
Este projeto é mantido e patrocinado pelas empresas:
Hospedado por: