terça-feira, 17 de julho de 2012

Autenticação de Usuários PHP-MVC

Olá, tomando como base o post que mostrei um esquema simples p/ implementar MVC em programação PHPmostrarei neste post como fazer autenticação de usuários para acesso restrito.


Hoje em dia é bastante comum querermos ou precisarmos restringir ou personalizar algumas seções do site. 


Uma maneira de implementar isso é utilizando um objeto de Sessão ($_SESSION no caso do PHP) no lado do servidor e autenticar o usuário por meio de um formulário de login e senha.


E para isso é necessário: 
1 -Criar a estrutura no banco de dados  p/ armazenar o usuários;
2- Criar a interface de acesso e o controlador de autenticação


E nas páginas que vc quiser restringir basta fazer o seguinte : 
1 - Criar um arquivo que verifica o estado da sessão e manda p/ a pagina de Login, se já não estiver logado.
2 - Incluir esse arquivo <include,require,require_once> no começo de cada arquivo restrito


O Exemplo : 
De onde veio este exemplo existem clientes e funcionários.Ambos são pessoas.
Assim sendo temos a classe base Pessoa e as classes que estendem Pessoa que são Cliente e Funcionario. já detalhadas aqui 


E por questões de segurança e regras do negócio ambos serão tratados como usuários, onde as views são separadas, mas acessam os mesmos objetos (assim as regras ficam organizadas, evitando códigos duplicados)

Vamos a estrutura do banco :
Observe que temos a tabela cliente e funcionario e usuario é uma view
Figura 1 - estrutura do banco




// funcionario
CREATE  TABLE IF NOT EXISTS `mydb`.`funcionario` (
  `id` INT(11) NOT NULL AUTO_INCREMENT ,
  `nome` VARCHAR(150) CHARACTER SET 'utf8' COLLATE 'utf8_bin' NULL DEFAULT NULL ,
  `funcao` VARCHAR(150) CHARACTER SET 'utf8' COLLATE 'utf8_bin' NULL DEFAULT NULL ,
  `cpf` VARCHAR(11) CHARACTER SET 'utf8' COLLATE 'utf8_bin' NOT NULL ,
  `data_admissao` DATE NOT NULL ,
  `salario` DECIMAL(7,2) NOT NULL ,
  `rg` VARCHAR(15) CHARACTER SET 'utf8' COLLATE 'utf8_bin' NOT NULL ,
  `carteira_trab` VARCHAR(15) CHARACTER SET 'utf8' COLLATE 'utf8_bin' NULL DEFAULT NULL ,
  `pis` VARCHAR(11) CHARACTER SET 'utf8' COLLATE 'utf8_bin' NOT NULL ,
  `data_nascimento` DATE NOT NULL ,
  `senha` VARCHAR(45) NULL DEFAULT NULL ,
  `ativo` TINYINT(1) NOT NULL DEFAULT '1' ,
  `admin` TINYINT(1) NULL DEFAULT '0' ,
  `perfil_acesso` ENUM('Atendente','Gestor','Admin') NULL DEFAULT NULL ,
  `login` VARCHAR(45) NULL DEFAULT NULL ,
  `email` VARCHAR(120) NULL DEFAULT NULL ,
  PRIMARY KEY (`id`) )
ENGINE = InnoDB
AUTO_INCREMENT = 4
DEFAULT CHARACTER SET = utf8
COLLATE = utf8_general_ci

//view 
CREATE OR REPLACE VIEW `usuario` AS 
select `C`.`id` AS `id`,
`C`.`email` AS `login`, -- login do cliente é o email
`C`.`senha` AS `senha`,
'C' AS `tipo_usuario`, -- define C
`C`.`ativo` AS `ativo`,
'Cliente' AS `perfil_acesso` 
from `mydb`.`cliente` `C` 
union 
select `F`.`id` AS `id`,
`F`.`login` AS `login`, -- funcionario possui um campo login
`F`.`senha` AS `senha`,
'F' AS `tipo_usuario`, -- define F
`F`.`ativo` AS `ativo`,
`F`.`perfil_acesso` AS `perfil_acesso` -- le o perfil
 from `mydb`.`funcionario` `F`


Vamos a estrutura dos arquivos : 


Figura 2 - Organização dos arquivos






Vamos aos arquivos:


Models:
/app/model/Funcionario.php
<?
//  @arquivo = /app/model/Funcionario.php
//  MVC : model
//  objeto : Funcionario


require_once($_SERVER['DOCUMENT_ROOT'] . '/app/model/Pessoa.php');

class Funcionario extends Pessoa{

    var $funcao;
    var $cpf;
    var $dataAdmissao;
    var $salario;
    var $rg;
    var $carteiraTrab;
    var $pis;
    var $dataNascimento;
    var $senha;
    var $ativo;
    var $admin;
    var $perfilAcesso;
    var $login;
    var $email;
    
    public function Funcionario() {
        
    }
    public function getFuncao() {
        return $this->funcao;
    }

    public function setFuncao($funcao) {
        $this->funcao = $funcao;
    }

    public function getCpf() {
        return $this->cpf;
    }

    public function setCpf($cpf) {
        $this->cpf = $cpf;
    }

    public function getDataAdmissao() {
        return $this->dataAdmissao;
    }

    public function setDataAdmissao($dataAdmissao) {
        $this->dataAdmissao = $dataAdmissao;
    }

    public function getSalario() {
        return $this->salario;
    }

    public function setSalario($salario) {
        $this->salario = $salario;
    }

    public function getRg() {
        return $this->rg;
    }

    public function setRg($rg) {
        $this->rg = $rg;
    }

    public function getCarteiraTrab() {
        return $this->carteiraTrab;
    }

    public function setCarteiraTrab($carteiraTrab) {
        $this->carteiraTrab = $carteiraTrab;
    }

    public function getPis() {
        return $this->pis;
    }

    public function setPis($pis) {
        $this->pis = $pis;
    }

    public function getDataNascimento() {
        return $this->dataNascimento;
    }

    public function setDataNascimento($dataNascimento) {
        $this->dataNascimento = $dataNascimento;
    }

    public function getSenha() {
        return $this->senha;
    }

    public function setSenha($senha) {
        $this->senha = $senha;
    }

    public function getAtivo() {
        return $this->ativo;
    }

    public function setAtivo($ativo) {
        $this->ativo = $ativo;
    }

    public function getAdmin() {
        return $this->admin;
    }

    public function setAdmin($admin) {
        $this->admin = $admin;
    }

    public function getPerfilAcesso() {
        return $this->perfilAcesso;
    }

    public function setPerfilAcesso($perfilAcesso) {
        $this->perfilAcesso = $perfilAcesso;
    }

    public function getLogin() {
        return $this->login;
    }

    public function setLogin($login) {
        $this->login = $login;
    }
    public function getEmail() {
        return $this->email;
    }

    public function setEmail($email) {
        $this->email = $email;
    }



}
?>


/app/model/Usuario.php
<?
//  @arquivo = /app/model/Usuario.php
//  MVC : model
//  objeto : Usuario
//  OBS : não existe uma tabela usuario no banco, e sim uma view que é uma junção
//  das tabelas clientes e funcionarios
 

class Usuario {

    var $id;
    var $login;
    var $senha;
    var $tipoUsuario;
    var $ativo;
    var $perfilAcesso;

    public function Usuario() {
        $this->id=0;
//        $this->admin=0;
    }

    public function getId() {
        return $this->id;
    }

    public function setId($id) {
        $this->id = $id;
    }

    public function getLogin() {
        return $this->login;
    }

    public function setLogin($login) {
        $this->login = $login;
    }

    public function getSenha() {
        return $this->senha;
    }

    public function setSenha($senha) {
        $this->senha = $senha;
    }

    public function getTipoUsuario() {
        return $this->tipoUsuario;
    }

    public function setTipoUsuario($tipoUsuario) {
        $this->tipoUsuario = $tipoUsuario;
    }

    public function getAtivo() {
        return $this->ativo;
    }

    public function setAtivo($ativo) {
        $this->ativo = $ativo;
    }


    public function getPerfilAcesso() {
        return $this->perfilAcesso;
    }

    public function setPerfilAcesso($perfilAcesso) {
        $this->perfilAcesso = $perfilAcesso;
    }

    
}
?>

DAOs:
/app/dao/UsuarioDAO.php
<?
require_once($_SERVER['DOCUMENT_ROOT'] . '/lib/Conexao.php');
require_once($_SERVER['DOCUMENT_ROOT'] . '/app/model/Usuario.php');

class UsuarioDAO {
    public function UsuarioDAO(){}

    public function autenticar($login, $senha) {
        $Usuario = new Usuario();
        $conexao = new Conexao();
 $conexao->conecta();
        $sqlQuery = sprintf("Select id,login,senha,ativo,tipo_usuario,perfil_acesso from usuario where login='%s'
            and senha='%s'",$login,$senha);
//        die ($sqlQuery);
 $rs = $conexao->executeQuery($sqlQuery);
        if ($row=mysql_fetch_array($rs)) {
            $Usuario->setId($row['id']);
            $Usuario->setLogin($row['login']);
            $Usuario->setAtivo($row['ativo']);
            $Usuario->setTipoUsuario($row['tipo_usuario']);
            $Usuario->setPerfilAcesso($row['perfil_acesso']);
        }
        $conexao->desconecta();
        return $Usuario;
    }

    public function load(Usuario $user) {
        $conexao = new Conexao();
 $conexao->conecta();
        $sqlQuery = "Select id,senha,tipo_usuario from usuario where id=".$user->getId();
        $rs = $conexao->executeQuery($sqlQuery);
        if ($row=mysql_fetch_array($rs)) {
            $Usuario = new Usuario();
            $Usuario->setId($row['id']);
            $Usuario->setSenha($row['senha']);
            $Usuario->setTipoUsuario($row['tipo_usuario']);
        }
        $conexao->desconecta();
        return $Usuario;
        
    }

    public function atualizaSenhaCliente(Usuario $user, $novasenha) {
        $conexao = new Conexao();
 $conexao->conecta();
        $sqlQueryUpdate = "UPDATE cliente 
                SET 
                `senha`='".$novasenha."' 
                where id=".$user->getId();
        
        $rs = $conexao->executeUpdate($sqlQueryUpdate);
        $conexao->desconecta();
        
    }

    public function localizaPorEmail($email) {
        $conexao = new Conexao();
 $conexao->conecta();
        $sqlQuery = "Select id,senha,tipo_usuario from usuario where login='".$email."' ";
        $rs = $conexao->executeQuery($sqlQuery);
        $carregado=false;
        if ($row=mysql_fetch_array($rs)) {
            $Usuario = new Usuario();
            $Usuario->setId($row['id']);
            $Usuario->setSenha($row['senha']);
            $Usuario->setTipoUsuario($row['tipo_usuario']);
        
           $carregado=true;
        }
        $conexao->desconecta();
        
        if ($carregado){
            return $Usuario; 
        }
        
        else {
           return null; 
        }
        
    }

    public function localizaPorDoc($tipoDoc, $busca) {
        $conexao = new Conexao();
       $conexao->conecta();
        $carregado=false;
        $sqlQuery = "Select id,senha,tipo_usuario from usuario where $tipoDoc='".$busca."' ";
        $rs = $conexao->executeQuery($sqlQuery);
        if ($row=mysql_fetch_array($rs)) {
            $Usuario = new Usuario();
            $Usuario->setId($row['id']);
            $Usuario->setSenha($row['senha']);
            $Usuario->setTipoUsuario($row['tipo_usuario']);
        
           $carregado=true;
        }
        $conexao->desconecta();
        
        if ($carregado){
            return $Usuario; 
        }
        else {
           return null; 
        } 
        
    }
    
    
}?>


Controllers:
/app/controller/Usuario/autentica.php
<?
//  @arquivo = /app/view/Usuario/autentica.php - recebe as variaveis de login via post
session_start();

require_once($_SERVER['DOCUMENT_ROOT'] . '/app/model/Usuario.php');
require_once($_SERVER['DOCUMENT_ROOT'] . '/app/controller/UsuarioController.php');


$user = new Usuario();
$userC = new UsuarioController();

if (isset($_POST['data']['Usuario']['login'])) {

    $login = $_POST['data']['Usuario']['login'];
}

if (isset($_POST['data']['Usuario']['senha'])) {

    $senha = $_POST['data']['Usuario']['senha'];
}

$user = $userC->autenticar($login, $senha);

if ($user->getId() == 0) {
    header("Location: /login.php");
}

if (($user->getId() != 0) && ($user->getTipoUsuario() == "C")) {
    $_SESSION['user']['logado'] = true;
    $_SESSION['user']['tipoUsuario'] = $user->getTipoUsuario();
    $_SESSION['user']['login'] = $user->getLogin();
    $_SESSION['user']['idUsuario'] = $user->getId();
    $_SESSION['user']['perfilAcesso'] = $user->getPerfilAcesso();
    $_SESSION['user']['view'] = "/cliente.php";
    header("Location: /pages/cliente.php?section=usuario&action=home");
}

if (($user->getId() != 0) && ($user->getTipoUsuario() == "F")) {
    $_SESSION['user']['logado'] = true;
    $_SESSION['user']['tipoUsuario'] = $user->getTipoUsuario();
    $_SESSION['user']['login'] = $user->getLogin();
    $_SESSION['user']['idUsuario'] = $user->getId();
    $_SESSION['user']['perfilAcesso'] = $user->getPerfilAcesso();


    switch ($_SESSION['user']['perfilAcesso']) {
        case "Admin":
            $_SESSION['user']['view'] = "/pages/privado.php";
            header("Location: ".$_SESSION['user']['view']);
            break;
        case "Gestor":
            $_SESSION['user']['view'] = "/pages/privado.php";
            header("Location: ".$_SESSION['user']['view']);
            break;
        case "Atendente":
//            $_SESSION['user']['view'] = "/atendente.php";
            $_SESSION['user']['view'] = "/pages/privado.php";
            header("Location: ".$_SESSION['user']['view']);
            break;

        default : // se tiver erro no cadastro, ou não tiver essa info
            header("Location: /login.php");
            break;
    }
}?>
/app/controller/UsuarioController.php 
<?
//  @arquivo = /app/model/UsuarioController.php
//  MVC : controller
//  objeto : UsuarioController
 
 
require_once($_SERVER['DOCUMENT_ROOT'].'/lib/Conexao.php');
require_once($_SERVER['DOCUMENT_ROOT'].'/app/model/Usuario.php');
require_once($_SERVER['DOCUMENT_ROOT'].'/app/dao/UsuarioDAO.php');

class UsuarioController {
    
    function autenticar($login,$senha){
        
        $usuarioDAO = new UsuarioDAO();
        $Usuario = $usuarioDAO->autenticar($login,$senha); 
        return $Usuario;
    }

    public function load(Usuario $user) {
        $usuarioDAO = new UsuarioDAO();
        $Usuario = $usuarioDAO->load($user); 
        
        return $Usuario;
    }
    
    
    // DEVE SER USADO APENAS P/ CLIENTES
    public function atualizaSenha(Usuario $user, $novasenha){
        $usuarioDAO = new UsuarioDAO();
        $usuarioDAO->atualizaSenhaCliente($user, $novasenha);
    }
 
    
    public function localizaUsuario($email){
        
        $usuarioDAO = new UsuarioDAO();
        $Usuario = $usuarioDAO->localizaPorEmail($email); 
        return $Usuario;
    }

    public function localizaUsuarioPorDocumento($tipoDoc, $busca) {
        
       $usuarioDAO = new UsuarioDAO();
       $Usuario = $usuarioDAO->localizaPorDoc($tipoDoc, $busca); 
       
       return $Usuario;
    }
    
    
}
?>


Views:
/login.php
<?
session_start();
if (isset($_GET['action']) && $_GET['action'] == 'logout') {
    unset($_SESSION['user']['logado'], $_SESSION['user']['tipoUsuario'], $_SESSION['user']['login'], $_SESSION['user']['idUsuario'], $_SESSION['user']['perfilAcesso'], $_SESSION['user']['view']
    );
}
?>
<fieldset>
<form action="/app/controller/Usuario/autentica.php" method="POST" id="formLogin" name="formLogin">
   <label for="Login">Login:</label>
   <input name="data[Usuario][login]" maxlength="30" type="text" id="login"/>
   <label for="Senha">Senha:</label>
   <input name="data[Usuario][senha]" maxlength="15" type="password" id="senha"/>
   <div class="submit"><input  type="submit" value="@Login"/></div>
</form>
</fieldset>






/lib/Conexao.php --descrita aqui


Agora vc pode optar por 2 alternativas 
1 - Cria o arquivo trava.php p/ ser incluido em todas as paginas 
<?
// arquivo /lib/trava.php
session_start();
if (!isset($_SESSION['user']['logado'] ))  {
   header("Location: /login.php?message=2");
}?>

e em todas paginas :
<? require_once($_SERVER['DOCUMENT_ROOT'] ."/lib/trava.php"); ?> 

2 - ou implementa o seu próprio framework a partir daqui :  

 /lib/Utils.php
<?
//  @arquivo = /lib/Utils.php
//  MVC :  controller
//  objeto :
//  obs : as funções comuns ao projeto

function includeFile($file) {
    try {
        if (file_exists($file)) {
            require_once $file;
        } else {
            //throw new Exception("Arquivo '$file' n&atilde;o encontrado");
            throw new Exception("A operação deste modulo '$file' ainda  n&atilde;o foi implementada.");
        }
    } catch (Exception $ex) {
        echo $ex->getMessage();
    }
}

function includeNewFile($file) {
    try {
  $fileX = $_SERVER['DOCUMENT_ROOT'].$file;
//  die($fileX);
        if (file_exists($fileX)) {
            require_once $fileX;
        } else {
            //throw new Exception("Arquivo '$file' n&atilde;o encontrado");
            throw new Exception("A operação deste modulo '$file' ainda  n&atilde;o foi implementada.");
        }
    } catch (Exception $ex) {
        echo $ex->getMessage();
    }
}
?>


/pages/* ou views


opcoes.php
<?php
error_reporting('E_ALL');

if (!$_SESSION['user']["logado"]) {
 header("Location: /login.php");
}

require_once($_SERVER['DOCUMENT_ROOT'] .'/lib/Utils.php');
if (isset($_GET['section'])){
 $section= $_GET['section'];
 switch($section){
  
  case 'clientes':
   $model='Cliente';
   $controller='ClienteController';    
  break;
  case 'servicos':
   $model='Servico';
   $controller='ServicoController';
  break;
  case 'tarefas':
   $model='Tarefa';
   $controller='TarefaController';
  break;
  case 'usuario':
   $model='Usuario';
   $controller='UsuarioController';
  break;
                case 'funcionarios':
   $model='Funcionario';
   $controller='FuncionarioController';
                        
  break;
                case 'boletos':
   $model='Boleto';
   $controller='BoletoController';
                        
  break;
  
  default:
   $model='empty';
   $controller='empty';
   break;
 }

includeNewFile("/app/controller/".$controller.".php"); 
includeNewFile("/app/model/".$model.".php");

}
$pageAction=''; // pagina de acao
if (isset($_GET['action'])){
 $action= $_GET['action'];
 switch($action){
            /*
  case 'index':
   $pageAction='index.php';
  break;
  case 'add':
   $pageAction='add.php';
  break;
  case 'edit':
   $pageAction='edit.php';
  break;
  case 'remove':
   $pageAction='remove.php';
  break;
  case 'view':
   $pageAction='view.php';
  break;  
  case 'login':
   $pageAction='login.php';
  break;
 */

  default:
   $pageAction=$action.'.php';
  break;
 
            
  }

}
?>


view.php
<?php
if (isset($_GET['section'])){ 
 $section = $_GET['section'];

 if ($section!=''){
  try {
   includeNewFile("/app/view/".$model."/".$pageAction); 
  } 
  
 catch (Exception $err){
  echo $err->getMessage();
 
  }
 }
}
?>


/pages/privado.php essa é a página do funcionário que faz todos os includes
<?
session_start();
error_reporting('E_ALL');

if (!isset($_SESSION['user']['logado'] ))  {
   header("Location: /login.php?message=2");
}
// gestor, admin e atendente tem acesso a essa pagina
if (!isset($_SESSION['user']['perfilAcesso']) || $_SESSION['user']['perfilAcesso']=="Cliente"  )  {
   header("Location: /login.php?message=2");
}
require_once($_SERVER['DOCUMENT_ROOT'] ."/pages/opcoes.php");  
?>
<!doctype html>
<?php require_once($_SERVER['DOCUMENT_ROOT'] ."/static_includes/inc_conditionals.php"); ?>
<head>
<?php 
$pageName = "TITULO";
include $_SERVER['DOCUMENT_ROOT'] ."/static_includes/inc_head-meta.php"; 
?>
</head>

<body>
<div id="container" class="box_round_0 box_shadow_0">
  <?php include $_SERVER['DOCUMENT_ROOT'] ."/static_includes/inc_header.php"; ?>
  <div id="main" class="clearfix">
    <div id="content">
        <? require_once('view.php'); ?>
    </div></div>
<?php include $_SERVER['DOCUMENT_ROOT'] ."/static_includes/inc_footer.php"; ?>
  </div>

<?php include $_SERVER['DOCUMENT_ROOT'] ."/static_includes/inc_pos-scripts.php"; ?>
</body>
</html>    


/pages/cliente.php essa é a página do cliente que faz todos os includes obs:esse arquivo não aparece na foto do projeto acima
<?
session_start();
error_reporting('E_ALL');

if (!isset($_SESSION['user']['logado'] ))  {
   header("Location: /login.php?message=2");
}
// gestor, admin e atendente tem acesso a essa pagina
if (!isset($_SESSION['user']['perfilAcesso']) || $_SESSION['user']['perfilAcesso']!="Cliente"  )  {
   header("Location: /login.php?message=2");
}
require_once($_SERVER['DOCUMENT_ROOT'] ."/pages/opcoes.php");  
?>
<!doctype html>
<?php require_once($_SERVER['DOCUMENT_ROOT'] ."/static_includes/inc_conditionals.php"); ?>
<head>
<?php 
$pageName = "TITULO";
include $_SERVER['DOCUMENT_ROOT'] ."/static_includes/inc_head-meta.php"; 
?></head>

<body>
<div id="container" class="box_round_0 box_shadow_0">
  <?php include $_SERVER['DOCUMENT_ROOT'] ."/static_includes/inc_header.php"; ?>
  <div id="main" class="clearfix">
    <div id="content">
        <? require_once('view.php'); ?>
    </div>
 </div>
<?php include $_SERVER['DOCUMENT_ROOT'] ."/static_includes/inc_footer.php"; ?>
  </div>
<?php include $_SERVER['DOCUMENT_ROOT'] ."/static_includes/inc_pos-scripts.php"; ?>
</body>
</html>

e Pronto. As seções estão separadas.
Agora a base do site na url fica assim após o Login:
http://seudominio/pages/privado.php?section=clientes&action=index e o arquivo /static_includes/inc_header.php pode conter os links p/ as seções

/static_includes/inc_header.php
<?
if (isset($_SESSION['user']['logado'])) {
    $userLogado = $_SESSION['user']['logado'];
} else {
    $userLogado = false;
}
?>
<div>
 <nav>
 <ul class="main-menu">
            <li><a href="/index.php" <?php if ($pageName == 'Home') echo 'class="current"'; ?>>Home</a></li>
                    <? if ($userLogado) { ?>
                 <li><a href="<? echo $_SESSION['user']['view']; ?>?section=boletos&action=index" <?php if ($_GET['section'] == 'boletos') echo 'class="current"'; ?>>Boletos</a></li>
                 <li><a href="<? echo $_SESSION['user']['view']; ?>?section=funcionarios&action=index"<?php if ($_GET['section'] == 'funcionarios') echo 'class="current"'; ?> >Funcionarios</a> </li>
                <li><a href="<? echo $_SESSION['user']['view']; ?>?section=clientes&action=index"<?php if ($_GET['section'] == 'clientes') echo 'class="current"'; ?> >Clientes</a> </li>
                    <? } ?>

                </ul>
</nav>











Um comentário:

Mario Junior disse...

Boa tarde, voce poderia passar o script completo dessa aplicação?

ta meio dificil fazer funcionar aqui, atraves desses posts picados.

desde ja agradeço
juninho_1703@hotmail.com