Students Save 30%! Learn & create with unlimited courses & creative assets Students Save 30%! Save Now
Advertisement
  1. Code
  2. Angular
Code

Autenticação com Tokens Usando AngularJS & NodeJS

by
Difficulty:IntermediateLength:LongLanguages:

Portuguese (Português) translation by Erick Patrick (you can also view the original English article)

Final product image
What You'll Be Creating

Uma das partes mais importantes de uma aplicação é sua autenticação. Neste tutorial, discutiremos sobre os sistemas de autenticação com tokens e como eles diferem dos sistemas tradicionais de login. Ao fim deste tutorial, você terá um aplicativo de demonstração totalmente funcional, criado com AngularJS e NodeJS

Sistemas de Autenticação Tradicionais

Antes de seguirmos para o sistema de autenticação com tokens, é bom entendermos melhor os sistemas de autenticação tradicionais primeiro.

  1. O usuário provê um nome de usuário e uma senha através do formulário de login e clica em um botão de Login;
  2. Assim que a requisição é feita valida-se o usuário no back-end, consultando sua existência na base de dados. Se a requisição for válida, cria-se uma sessão usando a informação obtida da base de dados para, então, retornar a informação da sessão no cabeçalho da resposta, de modo que o ID da sessão seja salvo no navegador;
  3. Provê-se informação da sessão para ter acesso a áreas restritas da aplicação;
  4. Se a informação da sessão for válida, permite-se que o usuário acesse a área restrita especificado, retornando o HTML apropriado para a requisição;

Tudo corre bem até esse ponto. A aplicação web funciona muito bem e é capaz de autenticar os usuários para que tenham acesso às áreas restritas. Contudo, o que acontece quando se cria outro sistema cliente, um aplicativo para Android, por exemplo? Você será capaz de usar a aplicação atual para autenticar os usuários do aplicativo cliente móvel e servir o conteúdo restrito? Da forma atual, não. E existem dois motivos para isso:

  1. Sessões e cookies não fazem sentido no contexto de aplicações móveis. Você não pode compartilhar sessões e cookies criados no lado do servidor com clientes móveis;
  2. Na aplicação atual, retorna-se o HTML gerado. Em um cliente móvel, você precisa que um JSON ou XML seja incluído na resposta;

Neste caso, você precisa de uma aplicação que seja independente do cliente.

Autenticação com Tokens

Na autenticação com tokens, não usaremos cookies nem sessões. Um token será usado para autenticar o usuário em cada requisição ao servidor. Reprojetemos nosso primeiro cenário de modo que use a autenticação com tokens.

Ela terá o seguinte controle de fluxo:

  1. O usuário provê um nome de usuário e uma senha no formulário de login e clica no botão Login;
  2. Assim que a requisição é feita valida-se o usuário no back-end, consultando sua existência na base de dados. Se a requisição for válida, cria-se um token, usando a informação do usuário obtida da base de dados e o retornamos no cabeçalho de resposta, para que possamos guardá-lo no navegador usando armazenamento local;
  3. Provê-se o token em cada cabeçalho de requisição para áreas protegidas na aplicação;
  4. Se o token obtido no cabeçalho da requisição for válido, permite-se o acesso à área restrita requisitada, com retornando a resposta em formato JSON ou XML;

Neste caso, não temos quaisquer cookies ou sessões retornadas bem como quaisquer conteúdo HTML. Isso significa que podemos usar essa arquitetura para qualquer cliente que a aplicação precisar. Você pode ver o esquema da arquitetura logo abaixo:

O que seria esse JWT?

JWT

JWT significa JSON Web Token (em português, Token da Web em formato JSON) e é um formato de token usado em cabeçalhos de autorização. Esse token auxilia você no projeto de comunicação segura entre sistemas. Neste tutorial, chamaremos o JWT de "token do portador". Um token do portador consiste de três partes: cabeçalho, carga e assinatura.

  • O cabeçalho é a parte do token em que se guarda o tipo e método de criptografia, o qual também é criptografado com o método de base-64;
  • A carga inclui a informação em si. Você pode colocar qualquer tipo de informação, como dados do usuário, informações de produtos e coisas do tipo, tudo guardado e criptografado em base-64;
  • A assinatura consiste na combinação do cabeçalho, da carga e de uma chave secreta. A chave secreta deve ser mantida segura no lado do servidor;

Você pode ver o esquema do JWT e um token exemplo logo abaixo:

Você não precisa implementar um gerador de token do portador uma vez que é possível encontrar versões existentes para várias linguagens de programação. Você pode ver várias delas logo abaixo:

Linguagem URL da Biblioteca
NodeJS http://github.com/auth0/node-jsonwebtoken
PHP http://github.com/firebase/php-jwt
Java http://github.com/auth0/java-jwt
Ruby http://github.com/progrium/ruby-jwt
.NET http://github.com/AzureAD/azure-activedirectory-identitymodel-extensions-for-dotnet
Python http://github.com/progrium/pyjwt/

Exemplo Prático

Agora que já sabemos um pouco sobre autenticação com tokens, podemos continuar com um exemplo prático. Veja o esquema abaixo, o qual analisaremos em instantes:

  1. As requisições à API são realizadas por vários clientes, como aplicação web, cliente móveis, etc, para um propósito específico;
  2. As requisições são feitas à um serviço como https://api.suaaplicacaoexemplo.com. Se várias pessoas usam a aplicação, vários servidores podem ser necessários para servir a operação requisitada;
  3. Aqui, um balanceador de carga é usado para equilibrar as requisições de modo que use da melhor forma os servidores da aplicação no lado do servidor. Ao fazer uma requisição a https://api.suaaplicacaoexemplo.com, o balanceador de carga lidará com a requisição primeiro e então redirecionará o cliente para um servidor específico;
  4. Há uma aplicação e essa aplicação é implantada em vários servidores (server-1, server-2, ..., server-n). Toda vez que uma requisição é realizada a https://api.suaaplicacaoexemplo.com, a aplicação do lado do servidor interceptará o cabeçalho de autorização da requisição e extrairá a informação do token. Uma consulta à base de dados será feita usando este token. Se ele for válido e possuir a permissão necessário para acessar a área requisitada, tudo continuará. Caso contrário, será retornado um código de resposta do tipo 403 (que indica um estado de acesso proibido);

Vantagens

Autenticação com tokens tem várias vantagens que resolvem vários problemas. Seguem alguns deles:

  • Serviços Independentes de Clientes: Na autenticação com tokens, um token é transferido através dos cabeçalhos de requisição ao invés de manter a informação de requisição em cookies e/ou sessões. Isso significa que não há qualquer estado. Você pode realizar requisições ao servidor a partir de qualquer cliente que seja capaz de realizar requisições HTTP;
  • CDN. Nas aplicações mais recentes, visões são geradas no back-end e o conteúdo HTML é retornado para o navegador. Lógica do front-end depende de código back-end. Não há necessidade em se fazer isso. Por exemplo, se estiver trabalhando em uma agência de design que codifica seu HTML, CSS e JavaScript, você precisará pegar parte desse código front-end e migrá-lo para o back-end para que possa gerá-lo usando dados do servidor. Após algum tempo, seu HTML gerado diferirá bastante daquilo que a agência codificou. Em autenticação com tokens, você pode desenvolver o front-end de um projeto de forma separada do código back-end. Seu código back-end retornará JSON ao invés de algum HTML gerado no servidor e você colocará a versão comprimida (com gzip) do código front-end em um CDN. Ao acessar sua página, o conteúdo HTML será servido do CDN e o conteúdo da página será populado a partir dos serviços de API usando os cabeçalhos de autenticação com tokens;
  • Sem Cookie e/ou Sessões (ou Nada de CSRF). CSRF (Cross-site Request Forgery) é um dos grandes problema na segurança de aplicações web, porque não se verifica se uma requisição é confiável ou não. Para resolver esse problema, uma pilha de tokens é usada para ser enviada em cada requisição de formulários. Na autenticação com tokens, um token é usado no cabeçalho de autorização e o CSRF não inclui essa informação;
  • Gravação Pesistente de Token. Quanto uma operação de leitura, escrita ou remoção de sessão é feita na aplicação, ela realizará uma ação com arquivos no sistema de arquivos do sistema operacional no diretório temp, pelo menos na primeira vez. Digamos que você possui vários servidores e uma sessão é criada no primeiro servidor. Ao realizar outra requisição, ela é enviada para outro servidor, onde a informação de sessão não existe e você receberá uma resposta de "não autorização". Certo, é possível resolver esse problema com uma sessão persistente. Contudo, na autenticação com tokens, esse caso sequer existe. Não há necessidade de sessões persistentes, porque o token de requisição é interceptado em cada requisição de qualquer servidor;

Essas são as vantagens mais comuns de autenticação e comunicação com tokens. Esse é o fim do papo teórico sobre o assunto. É hora de um exemplo prático.

Exemplo de Aplicação

Veremos duas aplicações para demonstrar a autenticação com tokens:

  1. token-based-auth-back-end
  2. token-based-auth-front-end

No projeto back-end, termos implementações de serviços e os retornos destes serão no formato JSON. Não será retornada qualquer visão pelos serviços. No projeto front-end, teremos códigos em AngularJS e HTML, o qual será populado pelos serviços em AngularJS que farão requisições ao serviços do projeto back-end.

token-based-auth-back-end

No projeto back-end temos três arquivos principais:

  • package.json para a administração de dependências;
  • models\User.js conterá um modelo de usuários, User, que será usado para realizar as operações na base de dados sobre os usuários;
  • server.js é para inicialização e manipulação de requisições;

E é isso! Esse projeto é bem simples, assim você poderá entender o conceito base facilmente, sempre precisarmos irmos mais a fundo.

O arquivo package.json contém as dependências do projeto: express para a estrutura em MVC, body-parser para simular simular a manipulação de requisição POST no NodeJS, morgan para registro de requisições, mongoose para que nosso framework ORM possa conectar-se ao MongoDB, e o jsonwebtoken para a criação de tokens JWT a partir de nossos modelos User. Também há um atributo chamado engines que especifica que o projeto usa o NodeJS de versão maior ou igual a 0.10.0. Isso é útil para serviços PaaS, como o Heroku. Também falaremos desse tópico em outra seção.

Dissemos que geraríamos um token usando o conteúdo do modelo User. Esse modelo nos ajudará a realizar operações sobre o MongoDB. No arquivo User.js, definimos o esquema do usuário e o modelo User, este último utilizando um modelo do mongoose. Este modelo está pronto para operações com a base de dados.

Nossas dependências estão definidas e nosso modelo User também, então, combinemos eles para construir um serviço para manipular requisições específicas.

No NodeJS, você pode incluir um módulo em seu projeto usando a função require. Primeiro, precisamos importar os módulos necessários para dentro do projeto:

Nossos serviços serão servidos através de uma porta específica. Se alguma porta nas variáveis do ambiente do sistema for indicada, você pode usá-la. Em nosso caso, preferimos definir a porta 3001. Logo depois, o modelo User é incluído e a conexão com a base de dados é estabelecida para que possamos realizar algumas operações em relação ao modelo. Não esqueça de definir uma variável de ambiente—MONGO_URL—para a URL da conexão com a base de dados.

Na seção acima, criamos algumas configurações para a simulação de manipulação de requisições HTTP no NodeJS usando Express. Nós demos permissão de requisições que venham de domínios diferentes para que possamos criar um sistema independente de cliente. Se não quiser permitir isso, você se deparará com um erro CORS (Cross Origin Request Sharing) no navegador.

  • Access-Control-Allow-Origin permitido para todos os domínios;
  • Você pode realizar requisições POST e GET para este serviço;
  • Cabeçalhos X-Requested-With e content-type são permitidos;

Importamos todos os módulos necessários e definimos nossa configuração, assim, é hora de definir os manipuladores de requisição. No código acima, toda vez que você realização uma requisição POST ao endereço /authenticate com um nome de usuário e senha, você receberá um token JWT. Primeiro, a base de dados consultará pelo nome de usuário e senha. Se o usuário existir, os dados do usuário serão retornado com o token. Mas, e se não existir qualquer usuário para o conjunto de nome de usuário e senha informados??

Ao realizar uma requisição POST para /signin com um nome de usuário e senha, um novo usuário será criado usando os dados informados. Na linha 19, você pode ver que um novo token JSON é gerado usando o módulo jsonwebtoken, o qual foi atribuído à variável jwt. A parte de autenticação está pronta. E se quisermos acessar alguma área restrita? Como administrar o acesso a essas áreas?

Ao realizar um requisição GET para /me, você obterá informação do usuário atual, mas antes de retorná-la, a função ensureAuthorized será executada.

Nesta função, cabeçalhos de requisição são interceptados e o cabeçalho authorization é extraído. Se o token do portador existir nesse cabeçalho, ele será atribuído a req.token para que seja utilizado durante a requisição e a requisição pode continuar, apenas chamando a função next(). Se um token não existir, você receberá uma resposta 403 (Acesso Proibido). Voltemos ao manipulador /me e usemos a propriedade req.token para buscar os dados do usuário deste token. Toda vez que você cria um novo usuário, um token é gerado e salvo no modelo User correspondente da base de dados. Esses tokens são únicos.

Só teremos três manipuladores para esse projeto, que é bem simples. Após isso, você verá;

O aplicativo NodeJS pode parar se algum erro acontecer. Com o código acima, prevenimos essa parada e registramos esse erro na linha de comando. Finalmente, podemos iniciar o servidor, usando o trecho de código a seguir. 

Resumindo:

  • Módulos são importados;
  • Configuração são criadas;
  • Manipuladores de requisição são definidos;
  • Um mediador é definido para interceptar e proibir acesso a áreas restritas;
  • O servidor é inicializado;

E terminamos o serviço back-end. Para que ele seja usado por vários clientes, você pode implantá-lo em seus servidores ou, quem sabe, enviá-lo para o Heroku. Há um arquivo chamado Procfile no diretório raiz do projeto. Implantemos nosso serviço no Heroku.

Implantando no Heroku

Você pode clonar o projeto back-end deste repositório do GitHub.

Não discutirei sobre como criar um aplicativo no Heroku; você pode acessar esse artigo e verificar como criar um aplicativo no Heroku caso não o tenha feito antes. Após criar seu aplicativo no Heroku, você pode adicionar um destino para seu projeto atual, usando o comando a seguir:

Você clonou e adicionou um destino ao seu projeto. Após executar git add e git commit, você pode enviar seu código para o Heroku executando o comando git push heroku master. Se o envio for bem sucedido, o Heroku executará o comando npm install para baixar todas as dependências no diretório temp do Heroku. Após isso, ele iniciará sua aplicação e poderá usar seu serviço usando o protocolo HTTP.

token-based-auth-front-end

No projeto front-end, você verá um projeto criado com AngularJS. Só mencionarei aqui as principais partes do projeto front-end, porque o AngularJS não é algo que pode ser visto em apenas um tutorial.

Você pode clonar o projeto a partir deste repositório do GitHub. Nesse projeto, você verá a estrutura de diretório a seguir:

Folder Structure

A ngStorage.js é uma biblioteca que permite o AngularJS realizar operações de manipulação de armazenamento local. Além disso, temos o layout principal index.html e os parciais que estendem o layout principal e se encontram dentro do diretório partials. O arquivo controllers.js é para definirmos as ações do nosso controlador front-end. O arquivo services.js é para realizar requisições de serviço ao nosso projeto recém criado. Temos uma espécie de arquivo de inicialização, app.js, que contém as configurações e importações de módulos. Finalmente, temos o arquivo client.js para servir os arquivos HTML estáticos (ou só o arquivo index.html, neste caso); isso ajudará a servir arquivos HTML estáticos ao implantar em um lugar sem o servidor web Apache ou algo do tipo.

No arquivo HTML do layout principal, todos os arquivos JavaScript necessários são incluídos, sejam eles biblioteca relacionadas ao AngularJS ou nossos controladores, serviços e arquivo app customizados.

No código acima, o controlador HomeCtrl é criado e alguns módulos necessários são injetados, como o $rootScope$scope. Injeção de Dependência é uma das funcionalidades mais fortes do AngularJS. A $scope é a variável que interliga os controladores e visões no AngularJS e isso significa que você pode usar test em alguma visão se o tiver definido em algum controlador, assim $scope.test=.... 

Neste controlador, algumas funções utilitárias são definidas, como:

  • signin para configurar o botão de acesso no formulário de acesso
  • signup para a manipulação do formulário de cadastro
  • me para lidar com o botão Me no layout

No layout principal, na lista do menu principal, você pode ver o atributo data-ng-controller com o valor HomeCtrl. Isso significa que o elemento dom deste menu pode compartilhar seu escopo com o controlador HomeCtrl. Ao clicar no botão de acesso no formulário, a função de acesso (signin) no controlador será executada, e nesta função o serviço de cadastro será usado a partir do serviço principal, Main, que já estará injetado no controlador. 

A estrutura principal é visão -> controlador -> serviço. Este serviço torna bem simples as requisições Ajax ao projeto back-end para obter dados.

No trecho de código acima, você pode ver que as funções do serviço gostam de realizar requisições para autenticação. Você deve ter visto no arquivo controller.js que há funções como Main.me. Esse serviço Main foi injetado no controlador. Enquanto no controlador, os serviços pertencentes a este serviço podem ser chamados diretamente. 

Essas funções são simples requisições Ajax para nosso serviço, que criamos mais cedo. Não esqueça de alterar a url na variável baseUrl no código acima. Ao implantar o serviço no Heroku, você receberá um URL no estilo nome-do-aplicativo.herokuapp.com. No trecho acima, você atribuirá essa URL dessa forma var baseUrl = "appname.herokuapp.com"

Nas partes de cadastro ou acesso da aplicação, recebemos um token de portador que é salvo no armazenamento local. Toda vez que realizamos uma requisição ao serviço no back-end, precisamos passar esse token nos cabeçalhos. Você pode fazer isso através dos interceptadores do AngularJS.

No trecho de código acima, cada requisição é interceptada e um cabeçalho de autorização e seu valor são colocados inseridos.

No projeto front-end, temos algumas páginas parciais como signinsignupdetalhes do perfil e vb. Essas páginas parciais estão relacionadas a controladores específicos. Você pode ver essa relação no arquivo app.js:

É bem fácil entender o código acima. Ao acessar a URL / do seu serviço, a página home.html será gerada. Outro exemplo: se você acessar a URL /signup, a página signup.htmlserá gerada. Essa operação de geração da página será realizada no navegador, não no lado do servidor.

Conclusão

Você pode ver os projetos que discutimos aqui, em pleno funcionamento, acessando esse projeto de demonstração.

Sistemas de autenticação com tokens ajudam a construir sistemas de autenticação/autorização e, ao mesmo tempo, criar serviços independentes de clientes. Usando essa tecnologia, você apenas focará em seus serviços (ou APIs). 

A parte de autenticação/autorização será manipulada pelo sistema de autenticação com tokens, na forma de uma camada antes dos seus serviços. Você pode acessar e usar seus serviços a partir de quaisquer clientes, como navegadores, aplicativos Android, aplicativos iOS ou de um cliente desktop.

Seja o primeiro a saber sobre novas traduções–siga @tutsplus_pt no Twitter!

Advertisement
Advertisement
Looking for something to help kick start your next project?
Envato Market has a range of items for sale to help get you started.