A imaginação é mais importante que o conhecimento. (Albert Einstein)

logo Moraga  
Principal
Categorias
Ferramentas

telescópio

Semanal Mensal Geral
  1. Converter Byte em KB, MB, GB, TB, EB
  2. Verificar CPF com PHP
  3. Captcha em PHP
  4. Jogo da velha ou Tic tac toe
  5. Copiando tabelas e removendo registros duplicados no MySQL
  6. removeChild - Remover elementos HTML por Javascript
  7. createElement - Solução compatível com os navegadores
  8. Removendo linhas e espaços em branco de strings
  9. Apache Expires Header - Cache de imagens, Javascript e CSS
  10. Verificar se existe um valor no Array em Javascript
  1. Converter Byte em KB, MB, GB, TB, EB
  2. Verificar CPF com PHP
  3. Captcha em PHP
  4. Copiando tabelas e removendo registros duplicados no MySQL
  5. Jogo da velha ou Tic tac toe
  6. createElement - Solução compatível com os navegadores
  7. removeChild - Remover elementos HTML por Javascript
  8. Gerando Thumbnails com PHP
  9. Removendo linhas e espaços em branco de strings
  10. Apache Expires Header - Cache de imagens, Javascript e CSS
  1. Converter Byte em KB, MB, GB, TB, EB
  2. Captcha em PHP
  3. Jogo da velha ou Tic tac toe
  4. createElement - Solução compatível com os navegadores
  5. Copiando tabelas e removendo registros duplicados no MySQL
  6. Apache Expires Header - Cache de imagens, Javascript e CSS
  7. Removendo linhas e espaços em branco de strings
  8. removeChild - Remover elementos HTML por Javascript
  9. Verificar se existe um valor no Array em Javascript
  10. Gerando Thumbnails com PHP
e-mail Enviar por e-mail imprimir Imprimir
PHP

Desenvolvendo sistema de autenticação com PHP e Design Patterns

Por Alejandro Fernandez Moraga

Foto por <a rel="nofollow" href="http://www.flickr.com/photos/11304375@N07/2849896588/">Image Editor</a> Foto por Image Editor

Padrão Observer ou Observador permite que objetos interessados, observadores, sejam avisados de eventuais mudanças de estado ou eventos de um outro objeto, o observável.

Em um sistema de autenticação o observável é o ato de verificar se o usuário e senha estão corretos e os observadores podem ser persistência da autenticação (cookie ou sessão), geração de logs de acessos e de tentativas, envio de e-mail, entre outros.

Código

Interface do observável

interface Observable {
public function attach(Observer $observer);
public function detach(Observer $observer);
public function notify();
}

O objeto observável deve implementar três métodos: attach, detach e notify. O método attach adiciona os observadores e o método detach remove os observadores. Os observadores precisam ser notificados das mudanças de estado ou evento do objeto observável, o método notify é responsável por notificar os observadores das mudanças.

O observável é o ato de verificar se o usuário e senha estão corretos e os observadores podem ser persistência da autenticação (cookie, sessão), geração de logs, envio de e-mail, etc. O padrão Observer torna fácil gerenciar funcionalidades.

class Auth implements Observable {

private $data;
private $observers = array();

public function attach(Observer $observer) {
$this->observers[] = $observer;
}

public function detach(Observer $observer) {
$observers = array();

foreach ($this->observers as $item) {
if ($item === $observer)
continue;
$observers[] = $item;
}
$this->observers = $observers;
}

public function notify() {
foreach ($this->observers as $observer)
$observer->update($this);
}

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

public function login($username, $password) {
// Tratamento das Strings
$username = str_replace('"', "'", $username);
$password = str_replace('"', "'", $password);

$query = mysql_query("SELECT * FROM usuarios WHERE usuario = '{$username}' AND password = PASSWORD('{$password}')");

if (mysql_num_rows($query))
$this->data = mysql_fetch_object($query);

$this->notify();
return $this->data ? true : false;
}
}

A classe Auth é o objeto observável. O atributo $data armazena os dados da autenticação, que, por sua vez, serão utilizados pelos observadores. Esses dados podem ser: nome, e-mail, usuário ou qualquer outro dado que seja necessário. $observers é o Array de objetos observadores, attach adiciona objetos e detach remove objetos.

Notify percorre o array de observadores executando o método update e passando sua instância. Cada observador implementa o método update. Essa chamada de método de objetos distintos é conhecida na programação orientada a objetos por Polimorfismo, a semântica de uma interface é efetivamente separada da implementação que a representa. Um objeto pode implementar a persistência da autênticação por cookie, outro por sessão e outro pode armazenar logs no banco de dados. Os objetos executam diferentes ações.

Data é um método de acesso, retorna o valor do atributo privado $data. Esse método não é parte obrigatória do padrão observer, é uma forma de repassar os dados do observável aos observadores. Se um cookie com nome será gerado, é necessário saber qual nome será usado para gerar o cookie. O nome, assim como outros dados, são gerados por login e acessados por data.

Login é a ação observável, verifica se o usuário e senha existem, notifica os observadores de sua execução e retorna verdadeiro ou falso. Independente de existirem ou não os observadores são acionados, isso é válido tanto para gerar um log de acesso como para gerar um log de tentativa sem sucesso, ou até mesmo iniciar medidas de segurança.

interface Observer {
public function update(Observable $observable);
}

A interface define apenas um único método para os observadores, o método update. Esse método recebe por parâmetro um objeto observável e executa sua ação.

class CookiePersist implements Observer {

public function update(Obserable $observable) {
$data = $observable->data();

if ($data)
foreach ($data as $attr => $value)
setcookie($attr, $value, null, '/', '.seudominio.com');
}
}

CookiePersist gera cookies com os dados retornados por data do observável. Os cookie só são gerados se dada retornar um valor diferente de falso.

class SessionPersist implements Observer {

public function update(Observable $observable) {
$data = $observable->data();

if ($data)
foreach ($data as $attr => $value)
$_SESSION[$attr] = $value;
}
}

SessionPersist gera variáveis de sessão com os dados retornados por data do observável, similar a CookiePersist.

class Log implements Observer {

public function update(Observable $observable) {
$data = $observable->data();

if ($data) {
// gera log de acesso
}
else {
// gera log de tentativa de acesso
}
}
}

Log é um modelo para armazenamento de log de autenticação. O log pode ser dividido em: log acesso e log de tentativa. Diferente dos observadores CookiePersist e SessionPersist, Log executa uma ação mesmo que a autenticação tenha falhado.

Em um sistema de autenticação os observadores podem ser selecionados seguindo regras: o usuário externo é limitado a uma sessão e para o usuário interno é gerado cookie.

Esses foram apenas três exemplos de observadores que poderiam ser implementados em um sistema de autenticação. O importante é notar como o padrão Observer lida e separa diferentes recursos, sua forma de pensar em observável e observador. É possível adicionar, alterar e remover funcionalidades sem comprometer o sistema.

Exemplo

<?php

$auth = new Auth();
$auth->attach(new CookiePersist());

if ($auth->login($_POST['usuario'], $_POST['senha'])) {
// dados corretos
}
else {
// usuário e/ou senha inválidos
}

?>

Código fonte

<?php

interface Observable {
public function attach(Observer $observer);
public function detach(Observer $observer);
public function notify();
}

class Auth implements Observable {

private $data;
private $observers = array();

public function attach(Observer $observer) {
$this->observers[] = $observer;
}

public function detach(Observer $observer) {
$observers = array();

foreach ($this->observers as $item) {
if ($item === $observer)
continue;
$observers[] = $item;
}
$this->observers = $observers;
}

public function notify() {
foreach ($this->observers as $observer)
$observer->update($this);
}

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

public function login($username, $password) {
// Tratamento das Strings
$username = str_replace('"', "'", $username);
$password = str_replace('"', "'", $password);

$query = mysql_query("SELECT * FROM usuarios WHERE usuario = '{$username}' AND password = PASSWORD('{$password}')");

if (mysql_num_rows($query))
$this->data = mysql_fetch_object($query);

$this->notify();
return $this->data ? true : false;
}
}

interface Observer {
public function update(Observable $observable);
}

class CookiePersist implements Observer {

public function update(Obserable $observable) {
$data = $observable->data();

if ($data)
foreach ($data as $attr => $value)
setcookie($attr, $value, null, '/', '.seudominio.com');
}
}

class SessionPersist implements Observer {

public function update(Observable $observable) {
$data = $observable->data();

if ($data)
foreach ($data as $attr => $value)
$_SESSION[$attr] = $value;
}
}

class Log implements Observer {

public function update(Observable $observable) {
$data = $observable->data();

if ($data) {
// gera log de acesso
}
else {
// gera log de tentativa de acesso
}
}
}

?>
Comentários
  • Marcelo 22 de Janeiro de 2009 10:56

    Ótima classe, mas como se faz a proteçao das páginas? No exemplo só tempos como iniciar a autenticação :P

    Parabéns e abraços!

  • Alejandro Moraga (www.moraga.com.br) 22 de Janeiro de 2009 11:28

    Muito obrigado pelo comentário Marcelo. Vou escrever sobre como verificar a autenticação em páginas de acesso restrito.

    Mais uma vez obrigado!
    Abraços

  • Hard (http://www.hardinspire.com/blog) 23 de Outubro de 2009 17:12

    "Sem comentários" rs

    Cara, são sites e blogs que nem o seu que cala a boca de muitos que falam mau do PHP, da sua estrutura e etc.

    Esta de parabens!
    =)


Comentar
captcha