Usamos cookies para medir audiência e melhorar sua experiência. Você pode aceitar ou recusar a qualquer momento. Veja sobre o iMasters.
Boa tarde pessoal, faz algum tempo que trabalho com php. Ja criei alguns sistemas e tudo mais ( de forma procedural ).
Aprendi a trabalhar com orientação a objetos, e agora estou desenvolvendo uma estrutura MVC.
Minha duvida e a melhor e mais segura forma de fazer a checagem do login. Vi em alguns artigos o seguinte sistema.
O login e checado no Controlador principal através de herança, já que todos as seções passam pelo MainController:
Ex:
Maincontroller.php
class Main extends Userlogin {
__construct(){
Fazachecagem();
}
//classe
}
E correto trabalhar dessa forma? e melhor do que instanciar a classe Userlogin em cada arquivo pra checar a sessão?
Abracos
Gabriel, primeiramente obrigado pela atenção.
Me ajude a estruturar melhor isso:
temos 3 situações:
Para checar o login, sigo os passos:
1 - chamo o Login-controller
2 - no mesmo controller recupero os dados das sessões
3 - chamo a model-login com os atributos das sessões e faço a verificação com o "try"
4 - Se negativo, direciono para o login, senão, continua na página protegida
Está correto?
Verificar a página protegida:
Onde eu devo chamar a função nas paginas que quero proteger? criando uma instancia de objeto ( checar_login() ) em cada página? nos controllers de cada assunto?
Fazer o login pelo form:
Referente a essa parte, depois de ver alguns artigos, gostei da possibilidade de na action do form de login chamar o mesmo checar_login, mas ai, na hora de checar separaria as verificações quando existisse a SESSION e o POST. Evitaria ter uma funcao para checar o login e outra a sessao.
Conto com a colaboração.
abraços
Esse post, extenso, será bem teórico e mostrará uma forma que você pode proceder, não necessariamente a melhor ou necessária forma. Como não tive muito tempo para revisar, caso eu encontrar uma inconsistência, estarei realizando a revisão.
------------
Qualquer forma de validação do login/autenticação deve ser feita ou "consumida" no Model.
Outro ponto, a sessão ($_SESSION) é um tipo de storage, logo, coloque-o ou consuma-o no Model. Não há o porque de o Controller saber que os dados de autenticação do usuário são na sessão. De fato, apesar de ser o lugar mais comum, a sessão não é o único lugar aonde pode ser salva a autenticação. Se for analisar, a definição de sessão é um espaço de tempo em algum lugar, ou seja, entendemos como algo temporário ou que necessita ser mantido de forma temporária em algum lugar.
Sendo mais simplista, o Controller pode saber que quem valida a autenticação é o model UserModel, mas não há necessidade de saber as seguintes questões:
- Como é autenticado;
- Aonde é autenticado;Como a autenticação remete a um usuário, o controller sabe que quem realiza a autenticação é UserModel, mas ele não deve saber o que UserModel faz internamente, apenas deve conhecer sua interface. Na real, não é uma exigência possuir uma interface, MAS é importante saber o que é uma interface e sua finalidade.
A interface define um contrato, ou seja, a classe que implementa uma interface define o que ela pode fazer/realizar para o "mundo exterior". Nesse caso, define-se "mundo exterior" como os outros participantes que podem utilizar/consumir uma determinada classe/objeto.
Como estamos falando de autenticação, teremos uma interface de autenticação. Essa interface deverá ser implementada por quem prove a autenticação
interface AuthInterface {
/**
* Realiza a autenticaçao de um usuario
* @param string $user
* @param string $password
* @throws \RuntimeException Se nao for possivel realizar a autenticacao
**/
public function auth($user , $password);
}
A interface de autenticação define o que é necessário para realizar a autenticação. O bloco PHPDoc define algumas informações adicionais:
- Tipo de parâmetros;
- O que é retornado (que no caso é omitido pois não retorna nada);Agora falando em login, vamos definir o que a interface de UserModel deve implementar:
interface UserInterface {
/**
* Realiza o login de um usuario
* @param string $user
* @param string $password
* @throws \RuntimeException Se nao for possivel realizar a autenticacao
**/
public function login(\AuthInterface $auth , $user , $password);
/**
* Retorna o usuario autenticado
* @return \User
* @throws \RuntimeException Se nao existir usuario autenticado
**/
public function getUserAuthenticated();
/**
* Verifica se existe um usuario autenticado
* @return boolean
**/
public function hasUserAuthenticated();
/**
* Realiza o logout do usuario autenticado
**/
public function logout();
}
A interface AuthInterface é utilizada por UserModel, pois UserModel necessita autenticar o usuário, seja aonde o registro do usuário está (SGBD, WebService, ActiveDirectory, OpenLDAP, arquivo txt, etc..).
Essa é a interface que é utilizada pelo Controller (que eu vou chamar de LoginController). Logo, LoginController sabe que UserModel possui os seguintes métodos:
- getUserAuthenticated;
- hasUserAuthenticated;
- login;Também vale salientar que, o método login é o método dependente de AuthInterface, por isso ele exige a passagem do da implementação.
Como os métodos de UserInterface funcionam, não é de importância para LoginController, apenas que eles façam o que é proposto.
Para um exemplo mais funcional, aonde eu trabalho, utilizamos o active directory (AD) como repositório de usuários. Visto que temos muitos sistemas que necessitam de autenticação, foi desenvolvido um web service para a autenticação. Irei utilizar esse fluxo como objetivo de estudos.
Funcionalmente, o fluxo é o seguinte:
Controller -> Model -> Auth -> WebService -> ActiveDirectory;
Já as interfaces:
UserInterface -> AuthInterface -> WSDL;
O WSDL é o arquivo de definição do web service, basicamente é a sua interface.
O que cada participante vê:
Controller -> UserInterface;
UserModel -> AuthInterface;
Auth -> WSDL;
Agora a implementação das classes:
WebService
class WebService implements AuthInterface {
/**
* @var \SoapClient
**/
private $webService;
/**
* @param string $wsdl A URI/URL do wsdl
**/
public function __contruct($wsdl) {
$this->webService = new \SoapClient(
$wsdl,
array(
'trace' => true,
'exceptions' => true,
'cache_wsdl' => WSDL_CACHE_NONE,
'features' => SOAP_SINGLE_ELEMENT_ARRAYS
)
);
}
/**
* {@inheritsDoc}
**/
public function auth($user , $password) {
if(!$this->webService->auth($user , $password)) {
throw \RuntimeException('Usuário ou senha inválidos');
}
}
}
UserModel:
class UserModel implements UserInterface {
/**
* {@inheritsDoc}
**/
public function login(\AuthInterface $auth , $user , $password) {
$auth->auth($user , $password)
$_SESSION['user']['login'] = $user;
}
/**
* {@inheritsDoc}
**/
public function hasUserAuthenticated() {
return isset($_SESSION['user']);
}
/**
* {@inheritsDoc}
**/
public function getUserAuthenticated() {
if($this->hasUserAuthenticated()) {
return $_SESSION['user']['login'];
}
throw \RuntimeException('Não há usuário autenticado');
}
/**
* {@inheritsDoc}
**/
public function logout() {
if($this->hasUserAuthenticated()) {
unset($_SESSION['user']);
}
}
}
O uso:
if($_SERVER['REQUEST_METHOD']) {
try {
$userModel = new UserModel();
$userModel->login(
new WebService('http://ad.webservice.com.br/?wsdl')
$_POST['usuario'],
$_POST['senha']
);
echo 'Autenticação realizada com sucesso';
} catch (\Exception $exception) {
printf('Ocorreu algum problema na autenticação: %s' , $exception->getMessage());
}
}
Outros modos de utilização, agora sem a dependência de AuthInterface.
$userModel = new UserModel();
printf('Há um usuário autenticado? %s' , $userModel->hasUserAuthenticated() ? 'Sim' : 'Não' ));
Nesse exemplo, a dependência se torna totalmente desnecessária, uma vez que não é preciso consultar no webservice.
É importante salientar esse é apenas um exemplo didático e que não passei AuthInterface pelo construtor, pós é uma dependência apenas um método e não para todos. Eu não quero entrar no conceito de Injeção de Dependência, pois não é a proposta do tópico e acredito que aqui tenha o suficiente para você entender estudar (http://www.phptherightway.com/#dependency_injection).
Existe diversos outros modos de abordar o problema, como mudar o fluxo, utilizando o seguinte:
AuthInterface -> UserInterface -> AuthInterface -> WSDL
Mas de que forma? Adicionarei o método login, na interface AuthInterface, para condizer com o que a interface propõe:
interface AuthInterface {
/**
* Realiza a autenticaçao de um usuário
* @param string $user
* @param string $password
* @throws \RuntimeException Se não for possível realizar a autenticação
**/
public function auth($user , $password);
/**
* Realiza o login de um usuário
* @param string $user
* @param string $password
* @throws \RuntimeException Se não for possível realizar a autenticação
**/
public function login($user , $password);
}
Ou seja, o método auth apenas realiza a autenticação (como ele é proposto) e o método login realiza o login completo do usuário.
Modificarei a implementação WebService:
class WebService implements AuthInterface {
/**
* @var \UserInterface
**/
private $userModel;
/**
* @var \SoapClient
**/
private $webService;
/**
* @param UserInterface $userModel
* @param string $wsdl A URI/URL do wsdl
**/
public function __contruct(\UserInterface $userModel , $wsdl) {
$this->userModel = $userModel
$this->webService = new \SoapClient(
$wsdl,
array(
'trace' => true,
'exceptions' => true,
'cache_wsdl' => WSDL_CACHE_NONE,
'features' => SOAP_SINGLE_ELEMENT_ARRAYS
)
);
}
/**
* {@inheritsDoc}
**/
public function auth($user , $password) {
if(!$this->webService->auth($user , $password)) {
throw \RuntimeException('Usuário ou senha inválidos');
}
}
/**
* {@inheritsDoc}
**/
public function login($user , $password) {
$this->userModel->login($this , $user , $password);
}
}
E agora o novo fluxo de uso utilizando dois métodos:
$userModel = new UserModel(new WebService('http://ad.webservice.com.br/?wsdl'));
if($_SERVER['REQUEST_METHOD']) {
try {
$auth = new WebService($userModel , 'http://ad.webservice.com.br/?wsdl'));
$auth->login($_POST['usuario'] , $_POST['senha']);
echo 'Autenticação realizada com sucesso';
} catch (\Exception $exception) {
printf('Ocorreu algum problema na autenticação: %s' , $exception->getMessage());
}
}
printf('Há um usuário autenticado? %s' , $userModel->hasUserAuthenticated() ? 'Sim' : 'Não' ));
Como a autenticação, nesse contexto, refere-se a autenticação de um usuário, é totalmente aceitável ter o método de autenticação dependente de UserInterface. Isso varia de contexto para contexto.
Uma forma de minimizar os impactos da injeção de dependência, é utilizar um dependency manager ou gerenciador de dependências (existe nos exemplos do link acima).
De fato, essa abordagem é errada. Gerada, principalmente, pela má interpretação da programação vertical (herança) e a programação horizontal (associação, agregação e composição).
Para entender a diferença, de forma simples, faça as seguintes afirmações:
Vamos a prática:
4 - O MainController usa um UserLogin.Nesse caso, as afiramações corretas são: 1 e 4;
De uma maneira mais geral:
Ao seu exemplo:
Controller
Model
Controller parte 2
View