Autenticação de usuários
Poucas ou nenhumas aplicações web não precisam de nenhum mecanismo para login de usuário ou verificação de privilégios de usuário. Neste capítulo, vamos falar sobre isso:
- login e logout do usuário
- autenticadores e autorizadores personalizados
Nos exemplos, usaremos um objeto da classe Nette\Security\User, que representa o usuário atual e que
você obtém ao passá-lo usando a injeção de dependência. Nos
apresentadores, basta ligar para $user = $this->getUser()
.
Autenticação
Autenticação significa ** login de usuário**, ou seja, o processo durante o qual a identidade de um usuário é
verificada. O usuário normalmente se identifica usando nome de usuário e senha. A verificação é realizada pelo chamado autenticador. Se o login falhar, ele lança
Nette\Security\AuthenticationException
.
Esta é a forma de efetuar o logout do usuário:
E verificar se o usuário está logado:
Simples, certo? E todos os aspectos de segurança são tratados pela Nette para você.
No apresentador, você pode verificar o login no método startup()
e redirecionar um usuário sem login para a
página de login.
Validade
O login do usuário expira junto com a expiração do repositório, que geralmente
é uma sessão (veja a configuração de expiração da sessão ). No entanto, também é
possível definir um intervalo de tempo mais curto após o qual o usuário é desconectado. O método
setExpiration()
, que é chamado antes de login()
, é usado para este fim. Fornecer uma seqüência com
um tempo relativo como parâmetro:
O método $user->getLogoutReason()
informa se o usuário foi desconectado porque o intervalo de tempo
expirou. Ele retorna ou a constante Nette\Security\UserStorage::LogoutInactivity
se o tempo expirou ou
UserStorage::LogoutManual
quando o método logout()
foi chamado.
Autenticador
É um objeto que verifica os dados de login, ou seja, geralmente o nome e a senha. A implementação trivial é a classe Nette\Security\SimpleAuthenticator, que pode ser definida na configuração:
Esta solução é mais adequada para fins de teste. Mostraremos a você como criar um autenticador que verificará as credenciais em relação a uma tabela de banco de dados.
Um autenticador é um objeto que implementa a interface Nette\Security\Authenticator com o método
authenticate()
. Sua tarefa é devolver a chamada identidade ou lançar uma exceção
Nette\Security\AuthenticationException
. Também seria possível fornecer um código de erro de grão fino
Authenticator::IdentityNotFound
ou Authenticator::InvalidCredential
.
A classe MyAuthenticator se comunica com o banco de dados através do Nette
Database Explorer e trabalha com a tabela users
, onde a coluna username
contém o nome de login do
usuário e a coluna password
contém hash. Após verificar o nome e a senha, ele retorna
a identidade com o ID do usuário, função (coluna role
na tabela), que mencionaremos mais
tarde, e um array com dados adicionais (no nosso caso, o nome do usuário).
Acrescentaremos o autenticador à configuração como um serviço do recipiente DI:
Eventos $onLoggedIn, $onLoggedOut
O objeto Nette\Security\User
tem eventos
$onLoggedIn
e $onLoggedOut
, assim você pode adicionar callbacks que são acionados depois de um login
bem sucedido ou depois que o usuário sai do sistema.
Identidade
Uma identidade é um conjunto de informações sobre um usuário que é devolvido pelo autenticador e que é então armazenado
em uma sessão e recuperado usando $user->getIdentity()
. Assim, podemos obter a identificação, funções e
outros dados do usuário à medida que os passamos no autenticador:
É importante ressaltar que quando o usuário faz logout usando $user->logout()
, identidade não é
apagada e ainda está disponível. Portanto, se a identidade existe, ela por si só não garante que o usuário também
esteja logado. Se quisermos excluir explicitamente a identidade, o usuário sai do sistema pelo endereço
logout(true)
.
Graças a isto, você ainda pode assumir qual usuário está no computador e, por exemplo, exibir ofertas personalizadas na loja virtual, no entanto, você só pode exibir seus dados pessoais após fazer o login.
A identidade é um objeto que implementa a interface Nette\Security\IIdentity, a implementação padrão é Nette\Security\SimpleIdentity. E como mencionado, a identidade é armazenada na sessão, portanto, se, por exemplo, mudarmos o papel de alguns dos usuários logados, os dados antigos serão mantidos na identidade até que ele se logue novamente.
Armazenamento para usuários logados
As duas informações básicas sobre o usuário, ou seja, se eles estão logados e sua identidade,
são geralmente carregadas na sessão. O que pode ser alterado. Para armazenar estas informações é responsável um objeto
implementando a interface Nette\Security\UserStorage
. Há duas implementações padrão, a primeira transmite dados
em uma sessão e a segunda em um cookie. Estas são as classes Nette\Bridges\SecurityHttp\SessionStorage
e
CookieStorage
. Você pode escolher o armazenamento e configurá-lo de forma muito conveniente na configuração de autenticação de segurança.
Você também pode controlar exatamente como será feita a economia de identidade (sleep) e o restabelecimento
(despertar). Tudo o que você precisa é que o autenticador implemente a interface
Nette\Security\IdentityHandler
. Isto tem dois métodos: sleepIdentity()
é chamada antes que a
identidade seja escrita para armazenamento, e wakeupIdentity()
é chamada após a leitura da identidade. Os métodos
podem modificar o conteúdo da identidade, ou substituí-la por um novo objeto que retorne. O método
wakeupIdentity()
pode até mesmo retornar null
, que registra o usuário fora.
Como exemplo, mostraremos uma solução para uma questão comum sobre como atualizar papéis de identidade logo após
o restabelecimento de uma sessão. No método wakeupIdentity()
, passamos os papéis atuais para a identidade, por
exemplo, a partir do banco de dados:
E agora voltamos ao armazenamento baseado em cookies. Ele permite criar um website onde os usuários podem fazer o login sem a
necessidade de usar sessões. Portanto, não é necessário escrever em disco. Afinal, é assim que o website que você está
lendo agora funciona, incluindo o fórum. Neste caso, a implementação do IdentityHandler
é uma necessidade.
Armazenaremos apenas um token aleatório representando o usuário logado no cookie.
Portanto, primeiro definimos o armazenamento desejado na configuração usando
security › authentication › storage: cookie
.
Acrescentaremos uma coluna authtoken
no banco de dados, na qual cada usuário terá uma seqüência completamente aleatória, única e indiscutível, de comprimento suficiente (pelo
menos 13 caracteres). O repositório CookieStorage
armazena apenas o valor $identity->getId()
no
cookie, assim, em sleepIdentity()
substituímos a identidade original por um proxy por authtoken
no ID,
ao contrário, no método wakeupIdentity()
restauramos toda a identidade do banco de dados de acordo com
authtoken:
Autenticações Independentes Múltiplas
É possível ter vários usuários independentes logados dentro de um site e uma sessão de cada vez. Por exemplo, se quisermos ter uma autenticação separada para o frontend e backend, apenas definiremos um espaço de nome de sessão único para cada um deles:
É necessário ter em mente que isto deve ser estabelecido em todos os lugares pertencentes ao mesmo segmento. Ao utilizar os apresentadores, colocaremos o namespace no ancestral comum – geralmente o BasePresenter. Para isso, estenderemos o método checkRequirements():
Autenticadores Múltiplos
Dividir uma aplicação em segmentos com autenticação independente geralmente requer autenticadores diferentes. Entretanto,
o registro de duas classes que implementam o Authenticator em serviços de configuração acionaria um erro porque a Nette não
saberia qual delas deveria estar ligada automaticamente ao objeto
Nette\Security\User
. É por isso que devemos limitar a auto-cablagem para eles com autowired: self
para
que ela seja ativada somente quando sua classe for especificamente solicitada:
Só precisamos definir nosso autenticador para o objeto Usuário antes de chamar o método de login() que normalmente significa no formulário de retorno de chamada: