Tags:

Controle de acesso com Angular-route

Algumas aplicações necessitam de um controle de acesso em sua página para evitar que usuários acessem recursos que não deveriam. Para evitar o acesso não autorizado, iremos utilizar algumas funções que o framework AngularJS com angular-route disponibilizam.

Dividi esse tutorial em 3 partes, sendo a primeira montando a estrutura para adicionarmos o angular com o angular-route, a segunda para implementarmos o controle de acesso e a terceira e última, para deixar nossa aplicação com uma aparência melhor e mais validações.

 

PASSO 1 – PRIMEIROS PASSOS

Como exemplo, montaremos um site de compra de livros somente para usuários cadastrados. Assim poderíamos controlar quem consegue comprar ou não os livros. Também, teremos um usuário que será o Administrador, que poderá ver a lista de usuários cadastrados (e removê-los), e também, remover os livros que estão cadastrados no sistema. Além desse controle, teremos um outro controle de quem pode ou não acessar um link específico.

Nosso objetivo:

Objetivo

Vamos usar a seguinte estrutura:

–App
   — partials
        — acessoNegado.html
        — home.html
        — livros.html
        — login.html
        — usuarios.html

   — app.css
   — app.js
   — index.html

Para esse exemplo foi utilizado um servidor local NodeJS utilizando a porta 8080 (http://localhost:8080).

Para começar, edite nossa index.html adicionando o Angularjs e o Angular-route.

Baixe o angularjs e o angular-route ou use um link (como no exemplo).

Index.html

<html>
   <head>
       <script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.3.0/angular.js"></script>
       <script src="http://code.angularjs.org/1.3.0/angular-route.js"></script>
   </head>
   <body>
[...]

 

No ‘module’ é preciso inserir a dependência do Angular-route adicionando o ‘ngRoute’, como pode ver no código abaixo.

Agora, crie todas as rotas.
Uma breve explicação para quem não sabe: É necessário configurar as rotas (urls), adicionando a página html que será exibida (templateUrl) quando o usuário acessar a url (when), e qual controlador (controller) será usado. No último caso, o ‘otherwise’, é utulizado para quando o usuário digitar um link que não esteja mapeado nesses casos abaixo.

app.js

angular.module('myApp', ['ngRoute'])
   .config(function ($routeProvider) {
       $routeProvider
           .when('/home', {
               templateUrl: 'partials/home.html',
               controller: 'homeController'
           })
           .when('/livros', {
               templateUrl: 'partials/livros.html',
               controller: 'livroController'
           })
           .when('/login', {
               templateUrl: 'partials/login.html',
               controller: 'loginController'
           })
           .when('/usuarios', {
               templateUrl: 'partials/usuarios.html',
               controller: 'usuariosController'
           })
           .when('/acessoNegado', {
               templateUrl: 'partials/acessoNegado.html',
               controller: 'acessoNegadoController'
           })
           .otherwise({ redirectTo: '/home'});
})
/* Criaremos um controller geral, e aqui adicionaremos algumas configurações*/
.controller('pageController', function ($scope) {

})
.controller('homeController', function ($scope) {

})
.controller('livroController', function ($scope) {   

})
.controller('loginController', function ($scope) {

})
.controller('usuariosController', function ($scope) {   

})
.controller('acessoNegadoController', function ($scope) {

})

Agora, crie todos os html’s  dentro de uma pastas chamada ‘partials’ adicionando o nome de cada tela no arquivo para testar se as rotas estão funcionando corretamente.

Pasta Partials

image08 

O próximo pequeno passo é editar o index.html para receber o arquivo app.js e olhar para as rotas do angular.
Adicione o nome do module no body (atributo “app”), uma div com o controlador da página e a div onde irá receber o conteúdo das outras páginas.

index.html

[...]
 <script src="app.js"></script>
</head>
 <body app=”myApp” ng-cloak>
   <div ng-controller="pageController">
     <div ng-view></div>
   </div>
 </body>
</html>

Entre no link http://localhost:8080 e verifique se todas as rotas estão funcionando corretamente como na Figura 1.
Obs: É necessário utilizar um servidor para utilizar o Angular-route.

Testando as rotas
Figura 1 – Testando as rotas

 

PASSO 2 – ADICIONANDO AS PERMISSÕES

Com as rotas funcionando, crie o menu e o serviço para logar.
Para facilitar e deixar o menu mais elegante, crie uma arquivo css (app.css) como feito no código abaixo.

app.css

header ul {
  list-style-type: none;
  margin: 0;
  padding: 0;
  overflow: hidden;
  background-color: #333;
  left: 0;
  width: 100%;
  position: fixed;
}

header li {
  float: left;
} 

header li a {
  display: block;
  color: white;
  text-align: center;
  padding: 14px 16px;
  text-decoration: none;
}

li a:hover {
  background-color: #111;
  text-decoration: none;
  color:aqua;
}

a:link, a:visited {
  text-decoration: none;
  color: white;
}

.active {
  background-color: dodgerblue;
  color: white;
}

.conteudo {
  padding-top:50px;
  background-color: #FCFEFF;
  width: 100%;
  height: 100%;
}

Agora adicione o app.css no index.html e também, adicione o bootstrap.css

[...]
<head>
  <link rel="stylesheet" href="https://maxcdn.bootstrapcdn.com/bootstrap/3.3.6/css/bootstrap.min.css" integrity="sha384-1q8mTJOASx8j1Au+a5WDVnPi2lkFfwwEAa8hDDdjZlpLegxhjVME1fgjWPGmkzs7" crossorigin="anonymous">
 <link rel="stylesheet" href="app.css">
[...]

Por fim, adicione o menu no index.html dentro do header

[...]
<div ng-controller="pageController">
  <header>
      <ul>
          <li class="active"><a href="#/home">Home</a></li>
          <li><a href="#/livros">Livros</a></li>
          <li><a href="#/usuarios">Usuários</a></li>
          <li style="float:right"><a href="#/login">Login</a></li>
          <li style="float:right"><a ng-click="logout()">Logout</a></li>
      </ul>
  </header>
  <div class="conteudo" ng-view></div>
[...]

 

Veja o resultado na Figura 2.

Tela com o angular-route funcionando
Figura 2 – Exibição do menu estilizado e o nome da rota

 

Bom, como será feito para o controlador permitir ou não o acesso?… Será necessário um sistema de login com o nível de acesso do usuário. Pra isso, no exemplo foram criados 3 usuários fictícios com uma variável boleana indicando se ele é administrador ou não.

Crie um service com os usuários fictícios e junto, uma função para validação do login.

app.js

[...]
.controller('acessoNegadoController', function ($scope) {})
.service('usuariosService', function ($rootScope, $location) {
  /*Esta função faz o papel de validação que seria feito no backend */
  this.validaLogin = function(user){

      //usuários fictícios que possam ser usados pela página e pra validar o login
      var usuarios = [{username:'Robson', password:'123', admin:true},
          {username:'Juliano', password:'123', admin:false},
          {username:'Bruno', password:'123', admin:false}]

      //Nesse trecho, um for para validar o login
      angular.forEach(usuarios, function(value, index){
          if(value.username == user.username &&
              value.password == user.password){
              delete value.password;
              $rootScope.usuarioLogado = value;
              $location.path('/home')
          }
      })
  }
  this.logout = function(){
      $rootScope.usuarioLogado = null;
      $location.path('/home')
  }
})
[...]

Notaram que foi adicionado o $rootScope? Devem estar se perguntando o motivo disso. Bom, o motivo de ser adicionado o usuário dentro de uma variável do $rootScope, é para poder acessá-lo em todas as páginas e serviços. Assim, fica mais fácil manipular o usuário sem usar sessão, token ou cookies. Também, assim é mais fácil para validar o tipo de acesso dele.

Com o serviço criado, agora será criado a página de login, e assim, começar o serviço para controle de acesso (finalmente).

login.html

<div class="container" style="padding-top:10px;">
  <h2> Faça o login </h2> <br/>
  <form class="form-horizontal" novalidate>
      <div class="form-group">
          <label class="control-label col-xs-2">Nome: </label>
          <div class="col-xs-6">
              <input class="form-control" type="text" ng-model="user.username"/>
          </div>
      </div>
      <div class="form-group">
          <label class="control-label col-xs-2">Senha:</label>
          <div class="col-xs-6">
              <input class="form-control col-xs-6" type="password" ng-model="user.password"/>
          </div>
      </div>
      <div class="form-group">
          <div class="col-sm-offset-2 col-xs-10">
              <input class="btn btn-default" type="submit" ng-click="logar(user)" />
          </div>
      </div>
  </form>
</div>

É possível ver no código acima a criação de um formulário simples com algumas classes do bootstrap. Foi adicionado 2 ng-model’s para ser possível pegar as informações que o usuário digitou na tela. Assim, quando o usuário clicar no botão ‘enviar’, irá chamar uma função no loginController chamada ‘logar()’ passando os dados digitados no formulário.

Próximo passo será implementar a função logar chamando o serviço que recém implementamos.

Será necessário passar o serviço usuariosService pro controller. A partir do serviço, será passado os dados digitados pelo usuário para uma função que valida o login. Caso estes dados fornecidos pelo usuário estejam corretos, o usuário será redirecionado para a Home. Segue o código:

app.js

[...]
.controller('loginController', function ($scope, usuariosService) {

  $scope.logar = function(user){
      usuariosService.validaLogin(user);
  }
})

.controller('usuariosController', function ($scope, usuariosService) {
[...]

 

Agora teste o login com o usuário Robson e senha 123.

Testando o login
Figura 3 – Testando o login

 

Após clicar enviar, se o nome e senha estiverem corretos será redirecionado para a home, caso contrário, não acontecerá nada.

Login funcionando
Figura 4 – Tela após ser redirecionado

 

Tudo funcionando. Agora será feito o objetivo principal deste post!!!! 

Será criado um ‘run’ no final do app.js. O método run sempre é chamado em qualquer evento que acontece na página, dentro dele vamos definir pra qual evento queremos que ele valide nosso usuário. No caso, será usado o evento que é chamado em toda troca de endereço.

Como tem 3 tipos de usuários, será necessário definir quais rotas eles podem acessar.

Foram criadas duas arrays com as rotas não permitidas para certo tipo de usuário.
Os tipos de usuários são: Usuário não logado, usuário comum e usuário administrador.

Como o administrador pode ter acesso a todas as páginas, não foi criado um array para ele.

app.js

.run(function ($rootScope, $location) {

  //Rotas que necessitam do login
  var rotasBloqueadasUsuariosNaoLogados = ['/usuarios', '/livros'];
  var rotasBloqueadasUsuariosComuns = ['/usuarios'];
  $rootScope.$on('$locationChangeStart', function () { //iremos chamar essa função sempre que o endereço for alterado

      /*  podemos inserir a logica que quisermos para dar ou não permissão ao usuário.
          Neste caso, vamos usar uma lógica simples. Iremos analisar se o link que o usuário está tentando acessar (location.path())
          está no Array (rotasBloqueadasUsuariosNaoLogados) caso o usuário não esteja logado. Se o usuário estiver logado, iremos
          validar se ele possui permissão para acessar os links no Array de strings 'rotasBloqueadasUsuariosComuns'
      */

      if($rootScope.usuarioLogado == null && rotasBloqueadasUsuariosNaoLogados.indexOf($location.path()) != -1){
          $location.path('/acessoNegado');

      }else
          if($rootScope.usuarioLogado != null &&
               rotasBloqueadasUsuariosComuns.indexOf($location.path()) != -1 &&
               $rootScope.usuarioLogado.admin == false){
          $location.path('/acessoNegado')
      }
  });
});

No código acima, possui um listener do evento ‘$locationChangeStart’. Toda vez que o usuário tentar acessar uma página, este evento será chamado antes de controller e a página html serem chamados. Também é possível usar o ‘$locationChangeSuccess’, que é chamado depois que a página e seu controller é carregado com sucesso. 

Hora de testar. logue com um usuário comum ( Nome: Bruno, senha: 123).

Após logar tente acessar a página de Livros e depois de Usuários.

Só lembrando que: com a implementação que feita dentro do .run, usuários comuns não tem acesso a página de Usuários, e caso ele tente acessá-la, deverá ser redirecionado para a home. Vale lembrar que, como está sendo trabalhado apenas com o $scope e $rootScope, toda vez que atualizar a página, será perdido o usuário logado e será necessário logar novamente. Caso não queira ter esse problema, uma solução é adicionar uma sessão e sempre jogar os dados dela pro $rootScope dentro do pageController.

Acessando a url Livros
Figura 5 – Acesso à página Livros

 

Livros, tudo ok.

E ao tentar acessar a página Usuários:

Acesso negado ao acessar usuários
Figura 6 – Acesso negado ao tentar acessar a página Usuários com um usuário comum

 

Pronto. Para o usuário logado, a implementação está funcionando. Os usuários não logados não irão conseguir acessar Livros e nem Usuários. Caso o usuário seja administrador (nome: Robson, Senha: 123), ele terá acesso a todas as páginas.

O objetivo principal foi concluído, porém, dá pra fazer mais e melhor, correto?…

O próximo passo é um resumo da versão completa desse exemplo (caso queira vê-lo, no final terá um link desse projeto no meu github).

 

PASSO 3 – RETOQUES FINAIS

Que tal incliur um ng-show no index.html para não mostrar o link de Livros ou Usuários para os usuários que não possuem permissões!? 

index.html

[...]
<li ng-show="$root.usuarioLogado != null"><a href="#/livros">Livros</a></li>

<li ng-show="$root.usuarioLogado.admin == true"><a href="#/usuarios">Usuários</a></li>

<li ng-hide="$root.usuarioLogado != null" style="float:right"><a href="#/login">Login</a></li>

<li ng-show="$root.usuarioLogado != null" style="float:right"><a ng-click="logout()">Logout</a></li>
[...]

Como o usuário está dentro do $rootScope, foi necessário usar o ‘$root’ para poder acessá-lo do html. Assim, podemos usar o ng-show para mostrar ou não alguns botões ou menus. Nesse caso, se o usuário não estiver logado, o botão Login ficará invisível. Assim como, se o usuário não estiver logado, o botão Logout estará invisível.

 No HTML, é chamado a função logout(), mas, ela não foi implementada dentro do pageController, apenas foi colocado no serviço. Possui duas formas de fazê-lo. Passando o usuariosService para uma variável que possa ser acessada pelo index. ($scope.usuariosService = usuariosService), ou criar uma função que chama a função logout dentro do usuariosService (como feito no exemplo). 

[...]
})

.controller('pageController', function ($scope, usuariosService) {
  $scope.logout = function(){
      usuariosService.logout();
  }
})

.controller('homeController', function ($scope) {
[...]

Além dessas pequenas alterações, a página Home foi modificada mostrando a mensagem ‘Seja bem vindo {{clienteLogado}}’ caso ele esteja logado, e outra mensagem para caso ele não esteja logado.

Também, foram criadas duas tabelas com diferentes permissões. Segue as imagens do resultado:

Página para usuário não logado
Figura 7 – Página home para usuário não logado

 

Figura 2 - Página home para usuário logado
Figura 8 – Página home para usuário logado
Página de Livros para usuários administradores
Figura 9 – Página de Livros para usuários administradores
Figura 4 - Página de Livros para usuários administradores
Figura 10 – Página de Usuários para usuários administradores
Página de Livros para usuários comuns
Figura 11 – Página de Livros para usuários comuns
Tentativa de acesso à página Usuários com um usuário comum.
Figura 12 -Tentativa de acesso à página Usuários com um usuário comum

 

Com isso, vemos que é possível fazer diversas validações apenas com o AngularJs sem qualquer dificuldade.

Veja o projeto completo em meu GitHub (clique aqui).

Até a próxima!

Por MICHAEL ANDRE DOMINGUES

Postado em: 25 de abril de 2016

Confira outros artigos do nosso blog

[Webinar] Profile de aplicações Java com Oracle Mission Control e Flight Recorder

24 de julho de 2017

Danival Calegari

Criando Mocks de serviços REST com SoapUI

27 de junho de 2017

Monise Costa

Three laws that enable agile software development

09 de março de 2017

Celso Gonçalves Junior

Medindo performance de uma API REST

21 de fevereiro de 2017

Monise Costa

Deixe seu comentário