Do not speak Portuguese? Translate this site with Google or Bing Translator
Tutorial do JSON Web Token um exemplo em Laravel e Angular

Posted on: December 15, 2022 12:01 PM

Posted by: Renato

Views: 1781

Tutorial do JSON Web Token: um exemplo em Laravel e AngularJS

A autenticação é uma das partes mais importantes de qualquer aplicativo da web. Durante décadas, cookies e autenticação baseada em servidor foram a solução mais fácil. No entanto, lidar com a autenticação em aplicativos móveis e de página única modernos pode ser complicado e exigir uma abordagem melhor. Uma das soluções mais conhecidas para problemas de autenticação de APIs é o JSON Web Token (JWT).

 

Com a popularidade crescente de aplicativos de página única, aplicativos móveis e serviços de API RESTful, a maneira como os desenvolvedores da Web escrevem o código de back-end mudou significativamente. Com tecnologias como AngularJS e BackboneJS, não estamos mais gastando muito tempo construindo marcações, em vez disso, estamos construindo APIs que nossos aplicativos de front-end consomem. Nosso back-end é mais sobre lógica de negócios e dados, enquanto a lógica de apresentação é movida exclusivamente para o front-end ou aplicativos móveis. Essas mudanças levaram a novas formas de implementação de autenticação em aplicativos modernos.

A autenticação é uma das partes mais importantes de qualquer aplicativo da web. Durante décadas, os cookies e a autenticação baseada em servidor foram a solução mais fácil. No entanto, lidar com a autenticação em aplicativos móveis e de página única modernos pode ser complicado e exigir uma abordagem melhor. As soluções mais conhecidas para problemas de autenticação de APIs são o OAuth 2.0 e o JSON Web Token (JWT).

Antes de entrarmos neste tutorial do JSON Web Token, o que exatamente é um JWT?

O que é um token da Web JSON?

Um JSON Web Token é usado para enviar informações que podem ser verificadas e confiáveis ​​por meio de uma assinatura digital. Ele compreende um objeto JSON compacto e seguro para URL, que é assinado criptograficamente para verificar sua autenticidade e que também pode ser criptografado se a carga útil contiver informações confidenciais.

Devido à sua estrutura compacta, o JWT geralmente é usado em Authorizationcabeçalhos HTTP ou parâmetros de consulta de URL.

Estrutura de um JSON Web Token

Um JWT é representado como uma sequência de valores codificados em base64url separados por caracteres de ponto.

 

Exemplo de token da web JSON em laravel e angularjs

 

Aqui está um exemplo de token JWT:

eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.
eyJpc3MiOiJ0b3B0YWwuY29tIiwiZXhwIjoxNDI2NDIwODAwLCJodHRwOi8vdG9wdGFsLmNvbS9qd3RfY2xhaW1zL2lzX2FkbWluIjp0cnVlLCJjb21wYW55IjoiVG9wdGFsIiwiYXdlc29tZSI6dHJ1ZX0.
yRQYnWzskCZUxPwaQupWkiUzKELZ49eM7oWxAQK_ZXw

O cabeçalho contém os metadados do token e contém minimamente o tipo de assinatura e o algoritmo de criptografia. (Você pode usar uma ferramenta de formatador JSON para embelezar o objeto JSON.)

Exemplo de Cabeçalho

{
  "alg": "HS256",
  "typ": "JWT"
}

Este cabeçalho de exemplo JWT declara que o objeto codificado é um JSON Web Token e que é assinado usando o algoritmo HMAC SHA-256.

Uma vez codificado em base64, temos a primeira parte do nosso JWT.

eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9

Carga útil (Reivindicações)

No contexto do JWT, uma declaração pode ser definida como uma declaração sobre uma entidade (normalmente, o usuário), bem como metadados adicionais sobre o próprio token. A declaração contém as informações que queremos transmitir e que o servidor pode usar para lidar adequadamente com a autenticação JSON Web Token. Existem várias reivindicações que podemos fornecer; isso inclui nomes de reivindicações registrados, nomes de reivindicações públicas e nomes de reivindicações privadas.

Declarações JWT registradas

Essas são as declarações registradas no registro de declarações de token da Web JSON da IANA . Essas declarações JWT não pretendem ser obrigatórias, mas sim fornecer um ponto de partida para um conjunto de declarações úteis e interoperáveis.

Esses incluem:

  • iss : O emissor do token
  • sub : o assunto do token
  • aud : o público do token
  • exp : tempo de expiração do JWT definido em tempo Unix
  • nbf : Hora “não antes” que identifica a hora antes da qual o JWT não deve ser aceito para processamento
  • iat : “Emitido em” horário, no horário do Unix, no qual o token foi emitido
  • jti : a reivindicação de ID do JWT fornece um identificador exclusivo para o JWT

Reivindicações públicas

As reivindicações públicas precisam ter nomes resistentes a colisões. Ao tornar o nome um URI ou URN, as colisões de nomenclatura são evitadas para JWTs em que o remetente e o destinatário não fazem parte de uma rede fechada.

Um exemplo de nome de declaração pública poderia ser: https://www.renatolucena.net/jwt_claims/is_admin, e a prática recomendada é colocar um arquivo nesse local descrevendo a declaração para que ela possa ser desreferenciada para documentação.

Créditos Privados

Os nomes de declarações privadas podem ser usados ​​em locais onde os JWTs são trocados apenas em um ambiente fechado entre sistemas conhecidos, como dentro de uma empresa. Essas são reivindicações que podemos definir por nós mesmos, como IDs de usuário, funções de usuário ou qualquer outra informação.

O uso de nomes de declaração que podem ter significados semânticos conflitantes fora de um sistema fechado ou privado está sujeito a colisão, portanto, use-os com cuidado.

É importante observar que queremos manter um token da Web o menor possível, portanto, use apenas os dados necessários em declarações públicas e privadas.

Carga útil de exemplo JWT

{
  "iss": "renatolucena.net",
  "exp": 1426420800,
  "https://www.renatolucena.net/jwt_claims/is_admin": true,
  "company": "Toptal",
  "awesome": true
}

Esta carga útil de exemplo tem duas declarações registradas, uma declaração pública e duas declarações privadas. Depois de codificado em base64, temos a segunda parte do nosso JWT.

eyJpc3MiOiJ0b3B0YWwuY29tIiwiZXhwIjoxNDI2NDIwODAwLCJodHRwOi8vdG9wdGFsLmNvbS9qd3RfY2xhaW1zL2lzX2FkbWluIjp0cnVlLCJjb21wYW55IjoiVG9wdGFsIiwiYXdlc29tZSI6dHJ1ZX0

Assinatura

O padrão JWT segue a especificação JSON Web Signature (JWS) para gerar o token assinado final. Ele é gerado combinando o JWT Header codificado e o JWT Payload codificado e assinando-o usando um algoritmo de criptografia forte, como HMAC SHA-256. A chave secreta da assinatura é mantida pelo servidor para que ele possa verificar os tokens existentes e assinar novos.

$encodedContent = base64UrlEncode(header) + "." + base64UrlEncode(payload);
$signature = hashHmacSHA256($encodedContent);

Isso nos dá a parte final do nosso JWT.

yRQYnWzskCZUxPwaQupWkiUzKELZ49eM7oWxAQK_ZXw

Segurança e Criptografia JWT

É fundamental usar TLS/SSL em conjunto com JWT para evitar ataques man-in-the-middle. Na maioria dos casos, isso será suficiente para criptografar a carga JWT se ela contiver informações confidenciais. No entanto, se quisermos adicionar uma camada adicional de proteção, podemos criptografar a própria carga JWT usando a especificação JSON Web Encryption (JWE).

Obviamente, se quisermos evitar a sobrecarga adicional de usar o JWE, outra opção é simplesmente manter as informações confidenciais em nosso banco de dados e usar nosso token para chamadas de API adicionais ao servidor sempre que precisarmos acessar dados confidenciais.

Por que a necessidade de tokens da Web?

Antes de podermos ver todos os benefícios do uso da autenticação JWT, precisamos ver como a autenticação era feita no passado.

Autenticação baseada em servidor

 

Autenticação baseada em servidor

 

Como o protocolo HTTP não tem estado, é necessário haver um mecanismo para armazenar as informações do usuário e uma maneira de autenticá-lo em todas as solicitações subsequentes após o login. A maioria dos sites usa cookies para armazenar o ID da sessão do usuário.

Como funciona

O navegador faz uma solicitação POST ao servidor que contém a identificação e a senha do usuário. O servidor responde com um cookie, que é definido no navegador do usuário e inclui um ID de sessão para identificar o usuário.

Em cada solicitação subseqüente, o servidor precisa localizar essa sessão e desserializá-la, porque os dados do usuário são armazenados no servidor.

Desvantagens da autenticação baseada em servidor

  • Difícil de dimensionar : o servidor precisa criar uma sessão para um usuário e mantê-la em algum lugar no servidor. Isso pode ser feito na memória ou em um banco de dados. Se tivermos um sistema distribuído, devemos garantir que usamos um armazenamento de sessão separado que não esteja acoplado ao servidor de aplicativos.

  • Compartilhamento de solicitação de origem cruzada (CORS) : ao usar chamadas AJAX para buscar um recurso de outro domínio (“origem cruzada”), podemos ter problemas com solicitações proibidas porque, por padrão, as solicitações HTTP não incluem cookies em cross-origin pedidos de origem.

  • Acoplamento com a estrutura da web : Ao usar a autenticação baseada em servidor, estamos vinculados ao esquema de autenticação da nossa estrutura. É realmente difícil, ou mesmo impossível, compartilhar dados de sessão entre diferentes estruturas da Web escritas em diferentes linguagens de programação.

Autenticação baseada em token

 

Autenticação baseada em token

 

A autenticação baseada em token/JWT é sem estado, portanto, não há necessidade de armazenar informações do usuário na sessão. Isso nos dá a capacidade de dimensionar nosso aplicativo sem nos preocuparmos com o local de login do usuário. Podemos usar facilmente o mesmo token para buscar um recurso seguro de um domínio diferente daquele em que estamos conectados.

Como funcionam os tokens da Web JSON

Um navegador ou cliente móvel faz uma solicitação ao servidor de autenticação contendo informações de login do usuário. O servidor de autenticação gera um novo token de acesso JWT e o retorna ao cliente. A cada solicitação a um recurso restrito, o cliente envia o token de acesso na string de consulta ou no Authorizationcabeçalho. O servidor então valida o token e, se for válido, retorna o recurso seguro ao cliente.

O servidor de autenticação pode assinar o token usando qualquer método de assinatura seguro. Por exemplo, um algoritmo de chave simétrica como HMAC SHA-256 pode ser usado se houver um canal seguro para compartilhar a chave secreta entre todas as partes. Alternativamente, um sistema assimétrico de chave pública, como o RSA, também pode ser usado, eliminando a necessidade de compartilhamento adicional de chaves.

Vantagens da autenticação baseada em token

Stateless, mais fácil de escalar : O token contém todas as informações para identificar o usuário, eliminando a necessidade do estado da sessão. Se usarmos um balanceador de carga, podemos passar o usuário para qualquer servidor, em vez de ficarmos vinculados ao mesmo servidor em que nos conectamos.

Reutilização : Podemos ter muitos servidores separados, rodando em múltiplas plataformas e domínios, reutilizando o mesmo token para autenticar o usuário. É fácil criar um aplicativo que compartilhe permissões com outro aplicativo.

Segurança JWT : como não estamos usando cookies, não precisamos nos proteger contra ataques de falsificação de solicitação entre sites (CSRF). Ainda devemos criptografar nossos tokens usando JWE se tivermos que colocar qualquer informação confidencial neles e transmitir nossos tokens por HTTPS para evitar ataques man-in-the-middle.

Desempenho : não há pesquisa no lado do servidor para localizar e desserializar a sessão em cada solicitação. A única coisa que precisamos fazer é calcular o HMAC SHA-256 para validar o token e analisar seu conteúdo.

Um exemplo de token da Web JSON usando Laravel 5 e AngularJS

Neste tutorial JWT vou demonstrar como implementar a autenticação básica usando JSON Web Tokens em duas tecnologias web populares: Laravel 5 para o código backend e AngularJS para o exemplo frontend Single Page Application (SPA). (Você pode encontrar a demonstração completa aqui e o código-fonte neste repositório GitHub para que possa acompanhar o tutorial.)

Este exemplo de token da Web JSON não usará nenhum tipo de criptografia para garantir a confidencialidade das informações transmitidas nas reivindicações. Na prática, isso geralmente é bom, porque o TLS/SSL criptografa a solicitação. No entanto, se o token contiver informações confidenciais, como o número do seguro social do usuário, ele também deverá ser criptografado usando JWE.

Exemplo de back-end do Laravel

Usaremos o Laravel para lidar com o registro do usuário, mantendo os dados do usuário em um banco de dados e fornecendo alguns dados restritos que precisam de autenticação para o aplicativo Angular consumir. Também criaremos um subdomínio de API de exemplo para simular o compartilhamento de recursos entre origens (CORS).

Inicialização de instalação e projeto

Para usar o Laravel, temos que instalar o gerenciador de pacotes Composer em nossa máquina. Ao desenvolver no Laravel, recomendo usar a “caixa” pré-embalada do Laravel Homestead do Vagrant. Ele nos fornece um ambiente de desenvolvimento completo, independentemente do nosso sistema operacional.

A maneira mais fácil de inicializar nosso aplicativo JWT Laravel é usar um instalador Laravel do pacote Composer.

composer global require "laravel/installer=~1.1"

Agora estamos todos prontos para criar um novo projeto Laravel executando laravel new jwt.

Para qualquer dúvida sobre este processo, consulte a documentação oficial do Laravel .

Após criarmos a aplicação básica do Laravel 5, precisamos configurar nosso Homestead.yaml, que irá configurar os mapeamentos de pastas e configuração de domínios para nosso ambiente local.

Exemplo de Homestead.yamlarquivo:

---
ip: "192.168.10.10"
memory: 2048
cpus: 1

authorize: /Users/ttkalec/.ssh/public.psk

keys:
    - /Users/ttkalec/.ssh/private.ppk
folders:
    - map: /coding/jwt
      to: /home/vagrant/coding/jwt
sites:
    - map: jwt.dev
      to: /home/vagrant/coding/jwt/public
    - map: api.jwt.dev
      to: /home/vagrant/coding/jwt/public
variables:
    - key: APP_ENV
      value: local

Depois de inicializar nossa caixa Vagrant com o vagrant upcomando e fazer login usando vagrant ssh, navegamos para o diretório do projeto definido anteriormente. No exemplo acima, isso seria /home/vagrant/coding/jwtAgora podemos executar o php artisan migratecomando para criar as tabelas de usuário necessárias em nosso banco de dados.

Instalando dependências do Composer

Felizmente, existe uma comunidade de desenvolvedores trabalhando no Laravel e mantendo muitos pacotes excelentes com os quais podemos reutilizar e estender nosso aplicativo. Neste exemplo, usaremos tymon/jwt-auth, de Sean Tymon, para manipulação de tokens no lado do servidor, e barryvdh/laravel-cors, de Barry vd. Heuvel, para lidar com CORS.

jwt-auth

Solicite o tymon/jwt-authpacote em nosso composer.jsone atualize nossas dependências.

composer require tymon/jwt-auth 0.5.* 

Adicione o JWTAuthServiceProviderà nossa app/config/app.phpmatriz de provedores.

'Tymon\JWTAuth\Providers\JWTAuthServiceProvider'

A seguir, no app/config/app.phparquivo, sob o aliasesarray, adicionamos a JWTAuthfachada.

'JWTAuth' => 'Tymon\JWTAuth\Facades\JWTAuth'

Por fim, queremos publicar a configuração do pacote usando o seguinte comando: php crafts config:publish tymon/jwt-auth

Os tokens da Web JSON são criptografados usando uma chave secreta. Podemos gerar essa chave usando o php artisan jwt:generatecomando. Ele será colocado dentro do nosso config/jwt.phparquivo. No ambiente de produção, no entanto, nunca queremos ter nossas senhas ou chaves de API dentro dos arquivos de configuração. Em vez disso, devemos colocá-los dentro das variáveis ​​de ambiente do servidor e referenciá-los no arquivo de configuração com a envfunção. Por exemplo:

'secret' => env('JWT_SECRET')

Podemos descobrir mais sobre este pacote e todas as suas configurações no Github .

laravel-cors

Solicite o barryvdh/laravel-corspacote em nosso composer.jsone atualize nossas dependências.

composer require barryvdh/laravel-cors 0.4.x@dev

Adicione o CorsServiceProviderà nossa app/config/app.phpmatriz de provedores.

'Barryvdh\Cors\CorsServiceProvider'

Em seguida, adicione o middleware ao nosso arquivo app/Http/Kernel.php.

'Barryvdh\Cors\Middleware\HandleCors'

Publique a configuração em um config/cors.phparquivo local usando o php artisan vendor:publishcomando.

Exemplo de cors.phpconfiguração de arquivo:

return [
   'defaults' => [
       'supportsCredentials' => false,
       'allowedOrigins' => [],
       'allowedHeaders' => [],
       'allowedMethods' => [],
       'exposedHeaders' => [],
       'maxAge' => 0,
       'hosts' => [],
   ],

   'paths' => [
       'v1/*' => [
           'allowedOrigins' => ['*'],
           'allowedHeaders' => ['*'],
           'allowedMethods' => ['*'],
           'maxAge' => 3600,
       ],
   ],
];

Roteamento e tratamento de solicitações HTTP

Por uma questão de brevidade, vou colocar todo o meu código dentro do arquivo routes.php que é responsável pelo roteamento do Laravel e pela delegação de requisições aos controllers. Normalmente, criaríamos controladores dedicados para lidar com todas as nossas solicitações HTTP e manter nosso código modular e limpo.

Vamos carregar nossa visualização AngularJS SPA usando

Route::get('/', function () {
   return view('spa');
});

Cadastro de usuário

Quando POSTsolicitamos /signupum nome de usuário e senha, tentaremos criar um novo usuário e salvá-lo no banco de dados. Após a criação do usuário, um JWT é criado e retornado via resposta JSON.

Route::post('/signup', function () {
   $credentials = Input::only('email', 'password');

   try {
       $user = User::create($credentials);
   } catch (Exception $e) {
       return Response::json(['error' => 'User already exists.'], HttpResponse::HTTP_CONFLICT);
   }

   $token = JWTAuth::fromUser($user);

   return Response::json(compact('token'));
});

Login do usuário

Quando fazemos uma POSTsolicitação /signincom nome de usuário e senha, verificamos se o usuário existe e retornamos um JWT por meio da resposta JSON.

Route::post('/signin', function () {
   $credentials = Input::only('email', 'password');

   if ( ! $token = JWTAuth::attempt($credentials)) {
       return Response::json(false, HttpResponse::HTTP_UNAUTHORIZED);
   }

   return Response::json(compact('token'));
});

Buscando um Recurso Restrito no Mesmo Domínio

Depois que o usuário estiver conectado, podemos buscar o recurso restrito. Criei uma rota /restrictedque simula um recurso que precisa de um usuário autenticado. Para fazer isso, o Authorizationcabeçalho da solicitação ou a string de consulta precisa fornecer o JWT para o back-end verificar.

Route::get('/restricted', [
   'before' => 'jwt-auth',
   function () {
       $token = JWTAuth::getToken();
       $user = JWTAuth::toUser($token);

       return Response::json([
           'data' => [
               'email' => $user->email,
               'registered_at' => $user->created_at->toDateTimeString()
           ]
       ]);
   }
]);

Neste exemplo, estou usando o jwt-authmiddleware fornecido no jwt-authpacote using 'before' => 'jwt-auth'Esse middleware é usado para filtrar a solicitação e validar o token JWT. Se o token for inválido, ausente ou expirado, o middleware lançará uma exceção que podemos capturar.

No Laravel 5, podemos capturar exceções usando o app/Exceptions/Handler.phparquivo. Usando a renderfunção, podemos criar respostas HTTP com base na exceção lançada.

public function render($request, Exception $e)
{
  if ($e instanceof \Tymon\JWTAuth\Exceptions\TokenInvalidException)
  {
     return response(['Token is invalid'], 401);
  }
  if ($e instanceof \Tymon\JWTAuth\Exceptions\TokenExpiredException)
  {
     return response(['Token has expired'], 401);
  }

  return parent::render($request, $e);
}

Se o usuário for autenticado e o token for válido, podemos retornar com segurança os dados restritos ao frontend via JSON.

Buscando recursos restritos do subdomínio da API

No próximo exemplo de token da Web JSON, adotaremos uma abordagem diferente para validação de token. Em vez de usar jwt-authmiddleware, trataremos as exceções manualmente. Quando fazemos uma POSTsolicitação para um servidor de API api.jwt.dev/v1/restricted, estamos fazendo uma solicitação de origem cruzada e precisamos habilitar o CORS no back-end. Felizmente, já configuramos o CORS no config/cors.phparquivo.

Route::group(['domain' => 'api.jwt.dev', 'prefix' => 'v1'], function () {
   Route::get('/restricted', function () {
       try {
           JWTAuth::parseToken()->toUser();
       } catch (Exception $e) {
           return Response::json(['error' => $e->getMessage()], HttpResponse::HTTP_UNAUTHORIZED);
       }

       return ['data' => 'This has come from a dedicated API subdomain with restricted access.'];
   });
});

Exemplo de front-end do AngularJS

Estamos usando AngularJS como front-end, contando com as chamadas de API para o servidor de autenticação de back-end Laravel para autenticação do usuário e dados de amostra, além do servidor de API para dados de exemplo de origem cruzada. Assim que formos para a página inicial do nosso projeto, o back-end servirá a resources/views/spa.blade.phpvisão que inicializará o aplicativo Angular.

Aqui está a estrutura de pastas do aplicativo Angular:

public/
  |-- css/
      `-- bootstrap.superhero.min.css
  |-- lib/
      |-- loading-bar.css
      |-- loading-bar.js
      `-- ngStorage.js
  |-- partials/
      |-- home.html
      |-- restricted.html
      |-- signin.html
      `-- signup.html
  `-- scripts/
      |-- app.js
      |-- controllers.js
      `-- services.js

Iniciando o aplicativo Angular

spa.blade.phpcontém o básico necessário para executar o aplicativo. Usaremos o Twitter Bootstrap para estilizar, juntamente com um tema personalizado do Bootswatch . Para obter algum feedback visual ao fazer uma chamada AJAX, usaremos o script angular-loading-bar , que intercepta solicitações XHR e cria uma barra de carregamento. Na seção de cabeçalho, temos as seguintes folhas de estilo:

<link rel="stylesheet" href="//maxcdn.bootstrapcdn.com/bootstrap/3.2.0/css/bootstrap.min.css">
<link rel="stylesheet" href="/css/bootstrap.superhero.min.css">
<link rel="stylesheet" href="/lib/loading-bar.css">

O rodapé de nossa marcação contém referências a bibliotecas, bem como nossos scripts personalizados para módulos, controladores e serviços Angular.

<script src="http://cdnjs.cloudflare.com/ajax/libs/jquery/2.1.1/jquery.min.js">script>
<script src="http://maxcdn.bootstrapcdn.com/bootstrap/3.3.2/js/bootstrap.min.js">script>
<script src="http://cdnjs.cloudflare.com/ajax/libs/angular.js/1.3.14/angular.min.js">script>
<script src="http://cdnjs.cloudflare.com/ajax/libs/angular.js/1.3.14/angular-route.min.js">script>
<script src="/lib/ngStorage.js">script>
<script src="/lib/loading-bar.js">script>
<script src="/scripts/app.js">script>
<script src="/scripts/controllers.js">script>
<script src="/scripts/services.js">script>
body>

Estamos usando ngStoragea biblioteca para AngularJS, para salvar os tokens no armazenamento local do navegador, para que possamos enviá-los a cada solicitação por meio do Authorizationcabeçalho.

No ambiente de produção, é claro, reduziríamos e combinaríamos todos os nossos arquivos de script e folhas de estilo para melhorar o desempenho.

Criei uma barra de navegação usando o Bootstrap que mudará a visibilidade dos links apropriados, dependendo do status de login do usuário. O status de entrada é determinado pela presença de uma tokenvariável no escopo do controlador.

<div class="navbar-header">
   <button type="button" class="navbar-toggle collapsed" data-toggle="collapse" data-target=".navbar-collapse">
       <span class="sr-only">Toggle navigationspan>
       <span class="icon-bar">span>
       <span class="icon-bar">span>
       <span class="icon-bar">span>
   button>
   <a class="navbar-brand" href="#">JWT Angular examplea>
div>
<div class="navbar-collapse collapse">
   <ul class="nav navbar-nav navbar-right">
       <li data-ng-show="token"><a ng-href="#/restricted">Restricted areaa>li>
       <li data-ng-hide="token"><a ng-href="#/signin">Sign ina>li>
       <li data-ng-hide="token"><a ng-href="#/signup">Sign upa>li>
       <li data-ng-show="token"><a ng-click="logout()">Logouta>li>
   ul>
div>

Roteamento

Temos um arquivo chamado app.jsque é responsável por configurar todas as nossas rotas de front-end.

angular.module('app', [
   'ngStorage',
   'ngRoute',
   'angular-loading-bar'
])
   .constant('urls', {
       BASE: 'http://jwt.dev:8000',
       BASE_API: 'http://api.jwt.dev:8000/v1'
   })
   .config(['$routeProvider', '$httpProvider', function ($routeProvider, $httpProvider) {
       $routeProvider.
           when('/', {
               templateUrl: 'partials/home.html',
               controller: 'HomeController'
           }).
           when('/signin', {
               templateUrl: 'partials/signin.html',
               controller: 'HomeController'
           }).
           when('/signup', {
               templateUrl: 'partials/signup.html',
               controller: 'HomeController'
           }).
           when('/restricted', {
               templateUrl: 'partials/restricted.html',
               controller: 'RestrictedController'
           }).
           otherwise({
               redirectTo: '/'
           });

Aqui podemos ver que definimos quatro rotas que são manipuladas por HomeControllerou RestrictedControllerCada rota corresponde a uma visualização HTML parcial. Também definimos duas constantes que contêm URLs para nossas solicitações HTTP para o back-end.

Solicitar Interceptador

O serviço $http do AngularJS nos permite comunicar com o backend e fazer requisições HTTP. No nosso caso, queremos interceptar todas as solicitações HTTP e injetá-las com um Authorizationcabeçalho contendo nosso JWT se o usuário for autenticado. Também podemos usar um interceptador para criar um manipulador de erros HTTP global. Aqui está um exemplo do nosso interceptador que injeta um token se estiver disponível no armazenamento local do navegador.

$httpProvider.interceptors.push(['$q', '$location', '$localStorage', function ($q, $location, $localStorage) {
   return {
       'request': function (config) {
           config.headers = config.headers || {};
           if ($localStorage.token) {
               config.headers.Authorization = 'Bearer ' + $localStorage.token;
           }
           return config;
       },
       'responseError': function (response) {
           if (response.status === 401 || response.status === 403) {
               $location.path('/signin');
           }
           return $q.reject(response);
       }
   };
}]);

controladores

No controllers.jsarquivo, definimos dois controladores para nosso aplicativo: HomeControllerRestrictedControllerHomeControllerlida com a funcionalidade de entrada, inscrição e logout. Ele passa os dados de nome de usuário e senha dos formulários de login e inscrição para o Authserviço, que envia solicitações HTTP para o back-end. Em seguida, ele salva o token no armazenamento local ou mostra uma mensagem de erro, dependendo da resposta do back-end.

angular.module('app')
   .controller('HomeController', ['$rootScope', '$scope', '$location', '$localStorage', 'Auth',
       function ($rootScope, $scope, $location, $localStorage, Auth) {
           function successAuth(res) {
               $localStorage.token = res.token;
               window.location = "/";
           }

           $scope.signin = function () {
               var formData = {
                   email: $scope.email,
                   password: $scope.password
               };

               Auth.signin(formData, successAuth, function () {
                   $rootScope.error = 'Invalid credentials.';
               })
           };

           $scope.signup = function () {
               var formData = {
                   email: $scope.email,
                   password: $scope.password
               };

               Auth.signup(formData, successAuth, function () {
                   $rootScope.error = 'Failed to signup';
               })
           };

           $scope.logout = function () {
               Auth.logout(function () {
                   window.location = "/"
               });
           };
           $scope.token = $localStorage.token;
           $scope.tokenClaims = Auth.getTokenClaims();
       }])

RestrictedControllerse comporta da mesma forma, apenas busca os dados usando as funções getRestrictedDatae no serviço.getApiDataData

   .controller('RestrictedController', ['$rootScope', '$scope', 'Data', function ($rootScope, $scope, Data) {
       Data.getRestrictedData(function (res) {
           $scope.data = res.data;
       }, function () {
           $rootScope.error = 'Failed to fetch restricted content.';
       });
       Data.getApiData(function (res) {
           $scope.api = res.data;
       }, function () {
           $rootScope.error = 'Failed to fetch restricted API content.';
       });
   }]);

O back-end é responsável por servir os dados restritos apenas se o usuário estiver autenticado. Isso significa que, para responder com os dados restritos, a solicitação desses dados precisa conter um JWT válido em seu Authorizationcabeçalho ou string de consulta. Se não for esse o caso, o servidor responderá com um código de status de erro 401 Não autorizado.

Serviço de Autenticação

O serviço Auth é responsável por fazer as solicitações HTTP de login e inscrição no back-end. Se a solicitação for bem-sucedida, a resposta conterá o token assinado, que é então decodificado em base64 e as informações de declarações de token incluídas são salvas em uma tokenClaimsvariável. Isso é passado para o controlador por meio da getTokenClaimsfunção.

angular.module('app')
   .factory('Auth', ['$http', '$localStorage', 'urls', function ($http, $localStorage, urls) {
       function urlBase64Decode(str) {
           var output = str.replace('-', '+').replace('_', '/');
           switch (output.length % 4) {
               case 0:
                   break;
               case 2:
                   output += '==';
                   break;
               case 3:
                   output += '=';
                   break;
               default:
                   throw 'Illegal base64url string!';
           }
           return window.atob(output);
       }

       function getClaimsFromToken() {
           var token = $localStorage.token;
           var user = {};
           if (typeof token !== 'undefined') {
               var encoded = token.split('.')[1];
               user = JSON.parse(urlBase64Decode(encoded));
           }
           return user;
       }

       var tokenClaims = getClaimsFromToken();

       return {
           signup: function (data, success, error) {
               $http.post(urls.BASE + '/signup', data).success(success).error(error)
           },
           signin: function (data, success, error) {
               $http.post(urls.BASE + '/signin', data).success(success).error(error)
           },
           logout: function (success) {
               tokenClaims = {};
               delete $localStorage.token;
               success();
           },
           getTokenClaims: function () {
               return tokenClaims;
           }
       };
   }
   ]);

Serviço de dados

Este é um serviço simples que faz solicitações ao servidor de autenticação, bem como ao servidor API, para alguns dados restritos fictícios. Ele faz a solicitação e delega retornos de chamada de sucesso e erro ao controlador.

angular.module('app')
   .factory('Data', ['$http', 'urls', function ($http, urls) {

       return {
           getRestrictedData: function (success, error) {
               $http.get(urls.BASE + '/restricted').success(success).error(error)
           },
           getApiData: function (success, error) {
               $http.get(urls.BASE_API + '/restricted').success(success).error(error)
           }
       };
   }
   ]);

Além deste tutorial de token da Web JSON

A autenticação baseada em token nos permite construir sistemas desacoplados que não estão vinculados a um esquema de autenticação específico. O token pode ser gerado em qualquer lugar e consumido em qualquer sistema que use a mesma chave secreta para assinar o token. Eles estão prontos para dispositivos móveis e não exigem que usemos cookies.

Os JSON Web Tokens funcionam em todas as linguagens de programação populares e estão ganhando popularidade rapidamente. Eles são apoiados por empresas como Google, Microsoft e Zendesk. Sua especificação padrão pela Internet Engineering Task Force (IETF) ainda está na versão preliminar e pode mudar ligeiramente no futuro.

Ainda há muito o que abordar sobre JWTs, como lidar com os detalhes de segurança e atualizar tokens quando eles expiram, mas o tutorial JSON Web Token deve demonstrar o uso básico e, mais importante, as vantagens de usar JWTs.

Fontes:

- https://www.toptal.com/express-js/nodejs-typescript-rest-api-pt-3

- https://www.toptal.com/web/cookie-free-authentication-with-json-web-tokens-an-example-in-laravel-and-angularjs

 


4

Share

Donate to Site


About Author

Renato

Developer

Add a Comment
Comments 0 Comments

No comments yet! Be the first to comment

Blog Search


Categories

OUTROS (15) Variados (109) PHP (130) Laravel (157) Black Hat (3) front-end (28) linux (113) postgresql (39) Docker (26) rest (5) soap (1) webservice (6) October (1) CMS (2) node (7) backend (13) ubuntu (54) devops (25) nodejs (5) npm (2) nvm (1) git (8) firefox (1) react (6) reactnative (5) collections (1) javascript (6) reactjs (7) yarn (0) adb (1) Solid (2) blade (3) models (1) controllers (0) log (1) html (2) hardware (3) aws (14) Transcribe (2) transcription (1) google (4) ibm (1) nuance (1) PHP Swoole (5) mysql (31) macox (4) flutter (1) symfony (1) cor (1) colors (2) homeOffice (2) jobs (3) imagick (2) ec2 (1) sw (1) websocket (1) markdown (1) ckeditor (1) tecnologia (14) faceapp (1) eloquent (14) query (4) sql (40) ddd (3) nginx (9) apache (4) certbot (1) lets-encrypt (3) debian (11) liquid (1) magento (2) ruby (1) LETSENCRYPT (1) Fibonacci (1) wine (1) transaction (1) pendrive (1) boot (1) usb (1) prf (1) policia (2) federal (1) lucena (1) mongodb (4) paypal (1) payment (1) zend (1) vim (4) ciencia (6) js (1) nosql (1) java (1) JasperReports (1) phpjasper (1) covid19 (1) saude (1) athena (1) cinnamon (1) phpunit (2) binaural (1) mysqli (3) database (42) windows (6) vala (1) json (2) oracle (1) mariadb (4) dev (12) webdev (24) s3 (4) storage (1) kitematic (1) gnome (2) web (2) intel (3) piada (1) cron (2) dba (18) lumen (1) ffmpeg (2) android (2) aplicativo (1) fedora (2) shell (4) bash (3) script (3) lider (1) htm (1) csv (1) dropbox (1) db (3) combustivel (2) haru (1) presenter (1) gasolina (1) MeioAmbiente (1) Grunt (1) biologia (1) programming (22) performance (3) brain (1) smartphones (1) telefonia (1) privacidade (1) opensource (3) microg (1) iode (1) ssh (3) zsh (2) terminal (3) dracula (1) spaceship (1) mac (2) idiomas (1) laptop (2) developer (37) api (4) data (1) matematica (1) seguranca (2) 100DaysOfCode (9) hotfix (1) documentation (1) laravelphp (10) RabbitMQ (1) Elasticsearch (1) redis (2) Raspberry (4) Padrao de design (4) JQuery (1) angularjs (4) Dicas (39) Kubernetes (2) vscode (2) backup (1) angular (3) servers (2) pipelines (1) AppSec (1) DevSecOps (4) rust (1) RustLang (1) Mozilla (1) algoritimo (1) sqlite (1) Passport (1) jwt (4) security (2) translate (1) kube (1) iot (1) politica (2) bolsonaro (1) flow (1) podcast (1) Brasil (1) containers (2) traefik (1) networking (1) host (1) POO (2) microservices (2) bug (1) cqrs (1) arquitetura (2) Architecture (3) sail (3) militar (1) artigo (1) economia (1) forcas armadas (1) ffaa (1) autenticacao (1) autorizacao (2) authentication (4) authorization (2) NoCookies (1) wsl (4) memcached (1) macos (2) unix (2) kali-linux (1) linux-tools (5) apple (1) noticias (2) composer (1) rancher (1) k8s (1) escopos (1) orm (1) jenkins (4) github (5) gitlab (3) queue (1) Passwordless (1) sonarqube (1) phpswoole (1) laraveloctane (1) Swoole (1) Swoole (1) octane (1) Structurizr (1) Diagramas (1) c4 (1) c4-models (1) compactar (1) compression (1) messaging (1) restfull (1) eventdrive (1) services (1) http (1) Monolith (1) microservice (1) historia (1) educacao (1) cavalotroia (1) OOD (0) odd (1) chatgpt (1) openai (3) vicuna (1) llama (1) gpt (1) transformers (1) pytorch (1) tensorflow (1) akitando (1) ia (1) nvidia (1) agi (1) guard (1) multiple_authen (2) rpi (1) auth (1) auth (1) livros (2) ElonMusk (2) Oh My Zsh (1) Manjaro (1) BigLinux (2) ArchLinux (1) Migration (1) Error (1) Monitor (1) Filament (1) LaravelFilament (1) replication (1) phpfpm (1) cache (1) vpn (1) l2tp (1) zorin-os (1) optimization (1) scheduling (1) monitoring (2) linkedin (1) community (1) inteligencia-artificial (2) wsl2 (1) maps (1) API_KEY_GOOGLE_MAPS (1) repmgr (1) altadisponibilidade (1) banco (1) modelagemdedados (1) inteligenciadedados (4) governancadedados (1) bancodedados (2) Observability (1) picpay (1) ecommerce (1)

New Articles



Get Latest Updates by Email