Ir para conteúdo

POWERED BY:

Arquivado

Este tópico foi arquivado e está fechado para novas respostas.

Fernando Pioli

Checar login em sistema MVC

Recommended Posts

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

Compartilhar este post


Link para o post
Compartilhar em outros sites

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:

- Se uma classe deve herdar de outra, ela "é um(a)";

- Se uma classe deve possuir uma dependência de outra classe, ela "usa um(a)";

 

Vamos a prática:

1 - O MainController é um Controller;
2 - O MainController usa um Controller;
3 - O MainController é um UserLogin;
4 - O MainController usa um UserLogin.
Nesse caso, as afiramações corretas são: 1 e 4;
De uma maneira mais geral:
- Controller: recebe a requisição e repassa para o responsável;
- Model: realiza a lógica de negócios, pode fazer uso de um storage e pode consultar outros models;
- View: recebe do Controller as informações para exibição, trata das regras de exibição.
Ao seu exemplo:
Controller
- Recebe a requisição e deve repassar para o responsável;
- A forma que a requisição é recebida pode varia, seja por:
- QueryString;
- HTTP Methods (POST, PUT);
- Linha de comando.
- Deve encontrar o responsável por tratar a requisição solicitada;
- Ao encontrar o responsável, os dados devem ser passados de maneira uniforme, independente da fonte de origem (ou seja, quem receber os dados, não precisa saber de onde eles vieram e nem como tratar a sua origem);
- Em âmbito geral, um Controller está ligado diretamente a uma View.
- O "responsável" pode ser um Model ou uma View. Caso for um Model, a informação é retornada para o Controller que repassará a informação para a View.
Model
- Aonde a "mágica" acontece. Do ponto de vista da programação, aonde tudo que é necessário para o Login é gerenciado;
- Do ponto de vista do método, recebe um usuário::string e uma senha::string;
- Consulta uma fonte de dados (SGBD, ActiveDirectory, OpenLDAP, WebService e/ou qualquer outra fonte necessária para a autenticação do usuário).
- Caso a autenticação ocorrer com sucesso, persiste as informações necessárias sobre o login, tais como:
- Sessão;
- Storage;
- Etc.
- Caso não for possível realizar a autenticação, interrompe a execução do script, normalmente com o lançamento de uma exceção (throw an exception). É recomendável o uso de exceções, ao invés de usar o retorno booleano (true/false) pois você pode ser bem específico com o problema que ocorreu ao realizar a autenticação. Com o booleano, você sabe apenas que se autenticou ou não, mas não o motivo de não ter a autenticação.
Controller parte 2
- Recebe as informações da autenticação.
- Caso for autenticado: realiza um possível redirecionamento, para uma nova controller;
- Caso não for autenticado: chama a View para dar o retorno ao usuário.
View
- Após todo o trabalho desempenhado, as informações retornadas ao Controller são enviadas para a View;
- A View cria o layout de exibição conforme as informações passadas pelo Controller.

Compartilhar este post


Link para o post
Compartilhar em outros sites

Gabriel, primeiramente obrigado pela atenção.

Me ajude a estruturar melhor isso:

temos 3 situações:

 

- Fazer o login pelo Form

- Checar o login

- Onde verificar login na página protegida

 

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

Compartilhar este post


Link para o post
Compartilhar em outros sites

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;
- Aonde a autenticação é salva.

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);
- O que e quando é lançada uma exceção.

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;
- logout.

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).

Compartilhar este post


Link para o post
Compartilhar em outros sites

  • Conteúdo Similar

    • Por Rafael_Ferreira
      Não consigo carregar a imagem do captcha do meu formulário. Foi testado com o xampp e easyphp. Também não carregou a imagem de outros captcha. 
       
       
    • Por luiz monteiro
      Olá, tudo bem?
       
      Estou melhorando meu conhecimento em php e mysql e, me deparei com o seguinte. A tabela da base de dados tem um campo do tipo varchar(8) o qual armazena números. Eu não posso alterar o tipo desse campo. O que preciso é fazer um select para retornar o números que contenham zeros a direita ou a esquerda.
      O que tentei até agora
       
      Ex1
      $busca = $conexao->prepare("select campo form tabela where (campo = :campo) ");
      $busca->bindParam('campo', $_REQUEST['campo_form']);
       
      Se a direita da string $_REQUEST['campo_form'] termina ou inicia com zero ou zeros, a busca retorna vazio.
      Inseri dados numéricos, da seguinte maneira para testar: 01234567;  12345670: 12345678: 12340000... entre outros nessa coluna. Todos os valores que não terminam ou não iniciam com zero ou zeros, o select funciona.
       
       
      Ex2
      $busca = $conexao->prepare("select campo form tabela where (campo = 0340000) ");
      Esse número está cadastrado, mas não retorna.
       
      Ex3
      $busca = $conexao->prepare("select campo form tabela where (campo = '02340001' ) ");
      Esse número está cadastrado, mas não retorna.
       
       
      Ex4
      $busca = $conexao->prepare("select campo form tabela where (campo like 2340000) ");
      Esse número está cadastrado, mas não retorna.
       
      Ex5
      $busca = $conexao->prepare("select campo form tabela where (campo like '12340000') ");
      Esse número está cadastrado, mas não retorna.
       
      Ex6
      $busca = $conexao->prepare("select campo form tabela where (campo like '"12340000"' ) ");
      Esse número está cadastrado, mas não retorna.
       
       
      Ex7
      $busca = $conexao->prepare("select campo form tabela where (campo like :campo) ");
      $busca->bindParam('campo', $_REQUEST['campo_form'])
      Não retorna dados.
       
      O  $_REQUEST['campo_form'] é envio via AJAX de um formulário. 
      Usei o gettype para verificar o post, e ele retorna string.
      Fiz uma busca com número 12345678 para verificar o que o select retorna, e também retrona como string.
       
      Esse tipo de varchar foi usado porque os números que serão gravados nesse campo,  terão zeros a direita ou na esquerda. Os tipos number do mysql não gravam zeros, então estou usando esse. O problema é a busca.
      Agradeço desde já.
       
       
    • Por daemon
      Boa tarde,
       
      Eu tenho uma rotina que faz uma leitura do arquivo .xml de vários sites.

      Eu consigo pegar o tópico e a descrição, e mostrar a imagem que esta na pagina do link.
      Para isso utilizo esta função:
      function getPreviewImage($url) { // Obter o conteúdo da página $html = file_get_contents($url); // Criar um novo objeto DOMDocument $doc = new DOMDocument(); @$doc->loadHTML($html); // Procurar pela tag meta og:image $tags = $doc->getElementsByTagName('meta'); foreach ($tags as $tag) { if ($tag->getAttribute('property') == 'og:image') { return $tag->getAttribute('content'); } } // Se não encontrar og:image, procurar pela primeira imagem na página $tags = $doc->getElementsByTagName('img'); if ($tags->length > 0) { return $tags->item(0)->getAttribute('src'); } // Se não encontrar nenhuma imagem, retornar null return null; } // Uso: $url = "https://example.com/article"; $imageUrl = getPreviewImage($url); if ($imageUrl) { echo "<img src='$imageUrl' alt='Preview'>"; } else { echo "Nenhuma imagem encontrada"; }  
      Mas estou com um problema, esta funcão funciona quando coloco em uma pagina de teste.php. Preciso mostrar em uma página inicial diversas fotos de todos os links. (No caso acima só funciona 1).
    • Por violin101
      Caros amigos, saudações.
       
      Por favor, me permita tirar uma dúvida com os amigos.

      Tenho um Formulário onde o Usuário digita todos os Dados necessários.

      Minha dúvida:
      --> como faço após o usuário digitar os dados e salvar, o Sistema chamar uma Modal ou mensagem perguntando se deseja imprimir agora ?

      Grato,
       
      Cesar
    • Por Carcleo
      Tenho uma abela de usuarios e uma tabela de administradores e clientes.
      Gostaria de uma ajuda para implementar um cadastro
       
      users -> name, login, passord (pronta) admins -> user_id, registratiom, etc.. client -> user_id, registratiom, etc...
      Queria ajuda para extender de user as classes Admin e Client
      Olhem como estáAdmin
      <?php namespace App\Models; use Illuminate\Database\Eloquent\Factories\HasFactory; use Illuminate\Database\Eloquent\Model; class Admin extends User {     use HasFactory;            protected $fillable = [         'name',         'email',         'password',         'registration'     ];      private string $registration;     public function create(         string $name,          string $email,          string $password,         string $registration     )     {         //parent::create(['name'=>$name, 'email'=>$email, 'password'=>$password]);         parent::$name = $name;         parent::$email = $email;         parent::$password = $password;         $this->registration = $registration;     } } User
      <?php namespace App\Models; // use Illuminate\Contracts\Auth\MustVerifyEmail; use Illuminate\Database\Eloquent\Factories\HasFactory; use Illuminate\Foundation\Auth\User as Authenticatable; use Illuminate\Notifications\Notifiable; use Illuminate\Database\Eloquent\Relations\BelongsToMany; class User extends Authenticatable {     /** @use HasFactory<\Database\Factories\UserFactory> */     use HasFactory, Notifiable;     static string $name;     static string $email;     static string $password;     /**      * The attributes that are mass assignable.      *      * @var list<string>      */     protected $fillable = [         'name',         'email',         'password',     ];          /**      * The attributes that should be hidden for serialization.      *      * @var list<string>      */     protected $hidden = [         'remember_token',     ];     /**      * Get the attributes that should be cast.      *      * @return array<string, string>      */     protected function casts(): array     {         return [             'email_verified_at' => 'datetime',             'password' => 'hashed',         ];     }          public function roles() : BelongsToMany {         return $this->belongsToMany(Role::class);     }       public function hasHole(Array $roleName): bool     {                 foreach ($this->roles as $role) {             if ($role->name === $roleName) {                 return true;             }         }         return false;     }         public function hasHoles(Array $rolesName): bool     {                 foreach ($this->roles as $role) {             foreach ($rolesName as $rolee) {             if ($role->name === $rolee) {                 return true;             }          }         }         return false;     }         public function hasAbility(string $ability): bool     {         foreach ($this->roles as $role) {             if ($role->abilities->contains('name', $ability)) {                 return true;             }         }         return false;     }     } Como gravar um Admin na tabela admins sendo que ele é um User por extensão?
      Tentei assim mas é claro que está errado...
      public function store(Request $request, Admin $adminModel) {         $dados = $request->validate([             "name" => "required",             "email" => "required|email",             "password" => "required",             "registration" => "required"         ]);         $dados["password"] =  Hash::make($dados["password"]);                  $admin = Admin::where("registration",  $dados["registration"])->first();                  if ($admin)              return                    redirect()->route("admin.new")                             ->withErrors([                                 'fail' => 'Administrador já cadastrados<br>, favor verificar!'                   ]);                            $newAdmin = $adminModel->create(                                    $dados['name'],                                    $dados['email'],                                    $dados['password'],                                    $dados['registration']                                 );         dd($newAdmin);         $adminModel->save();         //$adminModel::create($admin);                  return redirect()->route("admin.new")->with("success",'Cadastrado com sucesso');     }  
×

Informação importante

Ao usar o fórum, você concorda com nossos Termos e condições.