Front End

7 dez, 2015

Perfis de acesso com AngularJS

Publicidade

Existem diversas formas de implementar perfis de Acesso com AngularJS. Nesse artigo, vou mostrar uma possível abordagem de como restringir ou mostrar certas telas (ou fragmentos delas) para determinados perfis.

Obs.: Esse artigo assume um conhecimento mínimo do framework AngularJS. Se você ainda não possui esse conhecimento, sugiro primeiro aprender um pouco mais antes de continuar. Este tutorial completo pode te ajudar.

Primeiramente os requisitos do sistema:

  1. Existem os perfis: Admin, Visualização, Modificação e Comum;
  2. Um usuário pode ter mais de um perfil;
  3. Algumas telas e fragmentos de tela podem ter o acesso de mais de um perfil.

Primeiramente, vamos criar nosso controller de autenticação, porque é justamente no login que as informações do perfil do usuário serão passadas pelo back-end para o front-end:

function AuthController(AuthService) {
    var vm = this;
    
    vm.submit = submit;
    
    function submit(email, password) {
        AuthService.login(email, password).then(function(response) {
            // Lidar com a resposta de sucesso do back-end
        }).catch(function(response) {
           // Lidar com casos de erro
        });
    }
}

Agora, assumindo que o back-end nos passa um array de perfis relacionados ao usuário que fez o login, vamos adicionar nesse controller uma forma de guardar essa informação por meio de uma Factory:

function AuthController(AuthService, Profile) {
    var vm = this;
    
    vm.submit = submit;
    
    function submit(email, password) {
        AuthService.login(email, password).then(function(response) {
            // A factory Profile guardará as informações dos Perfis
            // do usuários que são passadas pelo back-end
            
            // Exemplo do array: roles: ['ADMIN', 'VISUALIZATION']
            Profile.setRoles(response.data.roles);
        }).catch(function(response) {
           // Lidar com casos de erro
        });
    }
}

Precisamos criar agora a estrutura da factory Profile, já com a primeira função que usamos no controller anterior, o setRoles:

function Profile() {
    var roles = [];
    
    return {
        setRoles: setRoles
    };
    
    function setRoles(roles) {
        this.roles = roles;
    }
}

Só com isso a factory ainda não tem muita utilidade. Vamos incrementá-la para que possamos verificar se o usuário possui determinados perfis de acesso:

function Profile() {
    var roles = [];
    
    return {
        setRoles: setRoles,
        isAdmin: isAdmin,
        isVisualization: isVisualization,
        isModification: isModification,
        isCommon: isCommon
    };
    
    function setRoles(roles) {
        this.roles = roles;
    }
    
    function isAdmin() {
        return contains(this.roles, 'ADMIN');
    }
    
    function isVisualization() {
        return contains(this.roles, 'VISUALIZATION');
    }
    
    function isModification() {
        return contains(this.roles, 'MODIFICATION');
    }
    
    function isCommon() {
        return contains(this.roles, 'COMMON');
    }
    
    function contains(array, element) {
	      return array && array.indexOf(element) > -1;
    }
}

Com o que foi construído até agora, já temos uma solução para perfis em fragmentos de telas. Se nós temos uma barra lateral na aplicação, por exemplo, podemos restringir ou mostrar certos links com base no perfil do usuário em questão:

<ul>
    <!-- Apenas Perfil Admin e/ou Visualização podem ver o link -->
    <li ng-if="profile.isAdmin() || profile.isVisualization()">
        <a ng-href="#/link1">Link 1</a>
    </li>
    <!-- Apenas Perfil Comum pode ver o link -->
    <li ng-if="profile.isCommon()">
        <a ng-href="#/link2">Link 2</a>
    </li>
    <!-- Sem restrição de acesso -->
    <li>
        <a ng-href="#/link3">Link 3</a>
    </li>
</ul>

Agora que já possuímos a restrição por fragmentos de tela, vamos construir uma solução para desabilitar (ou liberar) totalmente o acesso aos usuários por meio de rotas. A ideia é usar um resolver nas rotas para fazê-lo.

Primeiro, vamos montar nossa estrutura de rotas, para que três possíveis rotas existam: uma tela inicial, uma para admin e uma tela de usuários:

function Config($routeProvider) {
	.when('/', {
		templateUrl: '/auth/auth.html',
		controller: 'AuthController',
		controllerAs: 'vm'
	})
	.when('/admin', {
		templateUrl: '/admin/admin.html',
		controller: 'AdminController',
		controllerAs: 'vm'
	})
	.when('/users', {
		templateUrl: '/users/users.html',
		controller: 'UsersController',
		controllerAs: 'vm'
	});
}

As regras de acesso são:

  1. Tela inicial, de login: todos têm acesso;
  2. Tela para admin: apenas perfil Admin tem acesso;
  3. Tela de usuários: apenas perfis Admin e Modificação tem acesso.

Sabendo disso, vamos criar o resolver nas rotas necessárias e utilizar um serviço, que será implementado futuramente, que irá checar se o usuário possui pelo menos um dos perfis necessários para acessar a rota.

Portando, a nova estrutura ficará assim:

function Config($routeProvider) {
	.when('/', {
		templateUrl: '/auth/auth.html',
		controller: 'AuthController',
		controllerAs: 'vm'
	})
	.when('/admin', {
		templateUrl: '/admin/admin.html',
		controller: 'AdminController',
		controllerAs: 'vm',
		resolve: {
		    checkRoles: function(RouteAccessService, Profile) {
		        return RouteAccessService.checkRoles(Profile.isAdmin());
		    }
		}
	})
	.when('/users', {
		templateUrl: '/users/users.html',
		controller: 'UsersController',
		controllerAs: 'vm',
		resolve: {
		    checkRoles: function(RouteAccessService, Profile) {
		        return RouteAccessService.checkRoles(Profile.isAdmin() ||
		                                             Profile.isModification());
		    }
		}
	});
}

Basta apenas implementar o serviço bem simples RouteAccess, que deverá receber um valor booleano e se for falso (ou seja, o acesso está negado), deve redirecionar o usuário para a página principal:

function RouteAccess($location) {
    this.checkRoles = function(access) {
        if (!access) {
            $location.path('/');
        }
    }
}

Com isso, temos toda a estrutura montada para lidar com perfis de acesso com AngularJS. Se você tem uma outra abordagem, por favor indique nos comentários.

***

Esse artigfo foi originalmente publicado no Medium pessoal do autor. Confira aqui.