AngularJS: exemplo básico para usar autenticação em aplicativo de página única

100

Eu sou novo no AngularJS, passei pelo tutorial e comecei a entendê-lo.

Eu tenho um back-end para meu projeto pronto, onde cada um dos RESTendpoints precisa ser autenticado.

O que quero fazer
a.) Quero ter uma única página para meu projeto http://myproject.com.
b.) Uma vez que um usuário acessa a URL no navegador, com base em se o usuário está conectado ou não, ele é apresentado a uma página inicial / visualização ou página / visualização de login na mesma url http://myproject.com.
c.) se um usuário não estiver logado, ele preenche o formulário e o servidor define uma USER_TOKENsessão, de modo que todas as solicitações adicionais aos terminais serão autenticadas com base emUSER_TOKEN

Minhas confusões
a.) Como posso lidar com a autenticação do lado do cliente usando AngularJS? Eu vi aqui e aqui, mas não entendi como usá-los
b.) Como posso apresentar visualizações diferentes para o usuário com base em se o usuário está logado ou não na mesma urlhttp://myproject.com

Estou usando o angular.js pela primeira vez e realmente estou ficando confuso sobre como começar. Quaisquer conselhos e / ou recursos são muito apreciados.

sonhador
fonte
Por favor, dê uma olhada no artigo abaixo frederiknakstad.com/…
Ajay Beniwal
1
@MichaelCalkins apenas colocar um link não é construtivo. Você deve pelo menos dizer o que o link irá fornecer.
Dave Gordon
Meu b: Controle de acesso e autenticação AngularJS coderwall.com/p/f6brkg
Michael J. Calkins
A equipe da OAuth tem uma ótima biblioteca para este andreareginato.github.io/oauth-ng
Faktor 10

Respostas:

48

Criei um repositório github resumindo este artigo basicamente: https://medium.com/opinionated-angularjs/techniques-for-authentication-in-angularjs-applications-7bbf0346acec

repositório Github ng-login

Plunker

Vou tentar explicar da melhor maneira possível, espero ajudar alguns de vocês aí:

(1) app.js: Criação de constantes de autenticação na definição do aplicativo

var loginApp = angular.module('loginApp', ['ui.router', 'ui.bootstrap'])
/*Constants regarding user login defined here*/
.constant('USER_ROLES', {
    all : '*',
    admin : 'admin',
    editor : 'editor',
    guest : 'guest'
}).constant('AUTH_EVENTS', {
    loginSuccess : 'auth-login-success',
    loginFailed : 'auth-login-failed',
    logoutSuccess : 'auth-logout-success',
    sessionTimeout : 'auth-session-timeout',
    notAuthenticated : 'auth-not-authenticated',
    notAuthorized : 'auth-not-authorized'
})

(2) Serviço de autenticação: Todas as funções a seguir são implementadas no serviço auth.js. O serviço $ http é usado para se comunicar com o servidor para os procedimentos de autenticação. Também contém funções de autorização, isto é, se o usuário tem permissão para realizar uma determinada ação.

angular.module('loginApp')
.factory('Auth', [ '$http', '$rootScope', '$window', 'Session', 'AUTH_EVENTS', 
function($http, $rootScope, $window, Session, AUTH_EVENTS) {

authService.login() = [...]
authService.isAuthenticated() = [...]
authService.isAuthorized() = [...]
authService.logout() = [...]

return authService;
} ]);

(3) Sessão: Um singleton para manter os dados do usuário. A implementação aqui depende de você.

angular.module('loginApp').service('Session', function($rootScope, USER_ROLES) {

    this.create = function(user) {
        this.user = user;
        this.userRole = user.userRole;
    };
    this.destroy = function() {
        this.user = null;
        this.userRole = null;
    };
    return this;
});

(4) Controlador pai: considere isso como a função "principal" de seu aplicativo, todos os controladores herdam deste controlador e é a espinha dorsal da autenticação deste aplicativo.

<body ng-controller="ParentController">
[...]
</body>

(5) Controle de acesso: para negar o acesso em certas rotas, 2 etapas devem ser implementadas:

a) Adicione os dados das funções permitidas para acessar cada rota, no serviço $ stateProvider do roteador ui como pode ser visto abaixo (o mesmo pode funcionar para o ngRoute).

.config(function ($stateProvider, USER_ROLES) {
  $stateProvider.state('dashboard', {
    url: '/dashboard',
    templateUrl: 'dashboard/index.html',
    data: {
      authorizedRoles: [USER_ROLES.admin, USER_ROLES.editor]
    }
  });
})

b) Em $ rootScope. $ on ('$ stateChangeStart') adicione a função para evitar a mudança de estado se o usuário não estiver autorizado.

$rootScope.$on('$stateChangeStart', function (event, next) {
    var authorizedRoles = next.data.authorizedRoles;
    if (!Auth.isAuthorized(authorizedRoles)) {
      event.preventDefault();
      if (Auth.isAuthenticated()) {
        // user is not allowed
        $rootScope.$broadcast(AUTH_EVENTS.notAuthorized);
      } else {
        // user is not logged in
        $rootScope.$broadcast(AUTH_EVENTS.notAuthenticated);
      }
    }
});

(6) Interceptador de autenticação: implementado, mas não pode ser verificado no escopo deste código. Após cada solicitação $ http, este interceptor verifica o código de status, se um dos itens abaixo for retornado, ele transmite um evento para forçar o usuário a efetuar login novamente.

angular.module('loginApp')
.factory('AuthInterceptor', [ '$rootScope', '$q', 'Session', 'AUTH_EVENTS',
function($rootScope, $q, Session, AUTH_EVENTS) {
    return {
        responseError : function(response) {
            $rootScope.$broadcast({
                401 : AUTH_EVENTS.notAuthenticated,
                403 : AUTH_EVENTS.notAuthorized,
                419 : AUTH_EVENTS.sessionTimeout,
                440 : AUTH_EVENTS.sessionTimeout
            }[response.status], response);
            return $q.reject(response);
        }
    };
} ]);

PS Um bug com o preenchimento automático dos dados do formulário, conforme declarado no primeiro artigo, pode ser facilmente evitado adicionando-se a diretiva incluída em directives.js.

PS2 Este código pode ser facilmente ajustado pelo usuário, para permitir que diferentes rotas sejam vistas ou exibir conteúdo que não deveria ser exibido. A lógica DEVE ser implementada no lado do servidor, esta é apenas uma maneira de mostrar as coisas corretamente em seu ng-app.

Alex Arvanitidis
fonte
1
Tenho seguido seu guia para entender a lógica do lado do cliente. É realmente bom!! Eu perdi algo sobre destruir manualmente as sessões, mas temos que experimentar e quebrar coisas também!
Sebastialonso
~~ não tenho certeza se entendi corretamente essa linha: authService.login() = [...]esses colchetes representarão algo como $http.get(url, {uID, pwd}? ~~ ok, olhei para o êmbolo, era como eu disse XD
netalex
1
você pode expandir sua resposta para o lado do servidor?
consulta de
25

Eu gosto da abordagem e implementei no lado do servidor sem fazer nada relacionado à autenticação no front-end

Minha 'técnica' no meu aplicativo mais recente é ... o cliente não se importa com o Auth. Cada coisa no aplicativo requer um login primeiro, então o servidor sempre exibe uma página de login, a menos que um usuário existente seja detectado na sessão. Se session.user for encontrado, o servidor apenas envia index.html. Bam: -o

Procure o comentário de "Andrew Joslin".

https://groups.google.com/forum/?fromgroups=#!searchin/angular/authentication/angular/POXLTi_JUgg/VwStpoWCPUQJ

sonhador
fonte
3
se for uma API da web? Acho que não recebi sua resposta :(
Leandro De Mello Fagundes
1
E se você quiser exibir o nome de usuário? Ou se você estiver falando com um serviço com o nome de usuário nas URLs de endpoint?
perrygeo
2
desculpe, mas não entendo a resposta. como você lida com a sessão no angular? onde está session.user definido? você poderia fazer um exemplo de código disso, por favor? obrigado
François Romain
4
As sessões são tratadas no lado do cliente e não no lado do servidor, o cliente salva o token e o envia como parte de cada solicitação que faz. O servidor valida o token e processa a solicitação
daydreamer
4
Alguém que entenda isso poderia editar esta resposta para o resto de nós, por favor?
Alojz Janez
14

Eu respondi a uma pergunta semelhante aqui: Autenticação AngularJS + API RESTful


Eu escrevi um módulo AngularJS para UserApp que oferece suporte a rotas públicas / protegidas, reencaminhamento no login / logout, pulsações para verificações de status, armazena o token de sessão em um cookie, eventos, etc.

Você pode:

  1. Modifique o módulo e anexe-o à sua própria API ou
  2. Use o módulo junto com UserApp (uma API de gerenciamento de usuário baseada em nuvem)

https://github.com/userapp-io/userapp-angular

Se você usar o UserApp, não terá que escrever nenhum código do lado do servidor para as coisas do usuário (mais do que validar um token). Faça o curso sobre Codecademy para experimentá-lo.

Aqui estão alguns exemplos de como funciona:

  • Como especificar quais rotas devem ser públicas e qual rota é o formulário de login:

    $routeProvider.when('/login', {templateUrl: 'partials/login.html', public: true, login: true});
    $routeProvider.when('/signup', {templateUrl: 'partials/signup.html', public: true});
    $routeProvider.when('/home', {templateUrl: 'partials/home.html'});

    A .otherwise()rota deve ser definida para onde você deseja que seus usuários sejam redirecionados após o login. Exemplo:

    $routeProvider.otherwise({redirectTo: '/home'});

  • Formulário de login com tratamento de erros:

    <form ua-login ua-error="error-msg">
        <input name="login" placeholder="Username"><br>
        <input name="password" placeholder="Password" type="password"><br>
        <button type="submit">Log in</button>
        <p id="error-msg"></p>
    </form>
  • Formulário de inscrição com tratamento de erros:

    <form ua-signup ua-error="error-msg">
      <input name="first_name" placeholder="Your name"><br>
      <input name="login" ua-is-email placeholder="Email"><br>
      <input name="password" placeholder="Password" type="password"><br>
      <button type="submit">Create account</button>
      <p id="error-msg"></p>
    </form>
  • Link de logout:

    <a href="#" ua-logout>Log Out</a>

    (Termina a sessão e redireciona para a rota de login)

  • Acesse as propriedades do usuário:

    As propriedades do usuário são acessadas usando o userserviço, por exemplo:user.current.email

    Ou no modelo: <span>{{ user.email }}</span>

  • Oculte elementos que só devem ser visíveis quando conectado:

    <div ng-show="user.authorized">Welcome {{ user.first_name }}!</div>

  • Mostrar um elemento com base nas permissões:

    <div ua-has-permission="admin">You are an admin</div>

E para se autenticar em seus serviços de back-end, use user.token()para obter o token de sessão e enviá-lo com a solicitação AJAX. No back-end, use a API UserApp (se você usar UserApp) para verificar se o token é válido ou não.

Se precisar de ajuda, é só me avisar!

Timothy E. Johansson
fonte
Como eu "modificaria o módulo e o anexaria à sua própria API" ?
Pureferret
2

No angularjs, você pode criar a parte da IU, serviço, diretivas e todas as partes do angularjs que representam a IU. É uma boa tecnologia para trabalhar.

Como qualquer um que é novo nesta tecnologia e deseja autenticar o "Usuário", sugiro fazê-lo com o poder da API da web c #. para isso, você pode usar a especificação OAuth, que o ajudará a construir um mecanismo de segurança forte para autenticar o usuário. depois de construir o WebApi com OAuth, você precisa chamar essa api para o token:

var _login = function (loginData) {
 
        var data = "grant_type=password&username=" + loginData.userName + "&password=" + loginData.password;
 
        var deferred = $q.defer();
 
        $http.post(serviceBase + 'token', data, { headers: { 'Content-Type': 'application/x-www-form-urlencoded' } }).success(function (response) {
 
            localStorageService.set('authorizationData', { token: response.access_token, userName: loginData.userName });
 
            _authentication.isAuth = true;
            _authentication.userName = loginData.userName;
 
            deferred.resolve(response);
 
        }).error(function (err, status) {
            _logOut();
            deferred.reject(err);
        });
 
        return deferred.promise;
 
    };
 

e depois de obter o token, você solicita os recursos do angularjs com a ajuda do Token e acessa o recurso que é mantido seguro na Api da web com a especificação OAuth.

Por favor, dê uma olhada no artigo abaixo para obter mais ajuda: -

http://bitoftech.net/2014/06/09/angularjs-token-authentication-using-asp-net-web-api-2-owin-asp-net-identity/

Gurupreet
fonte
1

Acho que cada resposta JSON deve conter uma propriedade (por exemplo, {authenticated: false}) e o cliente deve testá-la sempre: se for false, o controlador / serviço Angular "redirecionará" para a página de login.

E o que acontecerá se o usuário pegar o JSON e mudar o bool para True?

Acho que você nunca deve confiar no lado do cliente para fazer esse tipo de coisa. Se o usuário não estiver autenticado, o servidor deve apenas redirecionar para uma página de login / erro.

Doum
fonte
2
Verifique isto: github.com/witoldsz/angular-http-auth - o interceptor verifica o código de status de resposta do servidor e se for 403 ('login obrigatório') ele transmite um evento, para que você possa capturá-lo dentro do aplicativo e exibir a caixa de login.
aherok de
10
Pare de responder uns aos outros usando respostas. É para isso que servem os comentários!
Soviut de
sugestão @aherok, seu comentário deve ser promovido a uma resposta, ele será votado para o topo no tempo. o resto é apenas ruído.
user237419
0

var _login = function (loginData) {
 
        var data = "grant_type=password&username=" + loginData.userName + "&password=" + loginData.password;
 
        var deferred = $q.defer();
 
        $http.post(serviceBase + 'token', data, { headers: { 'Content-Type': 'application/x-www-form-urlencoded' } }).success(function (response) {
 
            localStorageService.set('authorizationData', { token: response.access_token, userName: loginData.userName });
 
            _authentication.isAuth = true;
            _authentication.userName = loginData.userName;
 
            deferred.resolve(response);
 
        }).error(function (err, status) {
            _logOut();
            deferred.reject(err);
        });
 
        return deferred.promise;
 
    };
 

Nver Abgaryan
fonte