AngularJS: Podstawowy przykład użycia uwierzytelniania w aplikacji jednostronicowej

100

Jestem nowy w AngularJS i przejrzałem ich samouczek i poczułem to.

Mam gotowe zaplecze dla mojego projektu, w którym każdy z RESTpunktów końcowych musi zostać uwierzytelniony.

Co chcę zrobić
a.) Chcę mieć jedną stronę dla mojego projektu http://myproject.com.
b.) Gdy użytkownik trafi na adres URL w przeglądarce, na podstawie tego, czy jest zalogowany, czy nie, zostaje mu wyświetlona strona główna / widok lub strona logowania / widok pod tym samym adresem URL http://myproject.com.
c.) jeśli użytkownik nie jest zalogowany, wypełnia formularz, a serwer ustawia USER_TOKENsesję, więc wszystkie dalsze żądania do punktów końcowych będą uwierzytelniane na podstawieUSER_TOKEN

Moje nieporozumienia
a.) Jak mogę obsługiwać uwierzytelnianie po stronie klienta za pomocą AngularJS? Widziałem tutaj i tutaj, ale nie rozumiem, jak ich używać
b.) Jak mogę przedstawić użytkownikowi różne widoki na podstawie tego, czy użytkownik jest zalogowany, czy nie pod tym samym adresem URLhttp://myproject.com

Używam angular.js po raz pierwszy i naprawdę nie wiem, jak zacząć. Wszelkie porady i / lub zasoby są bardzo mile widziane.

marzyciel
źródło
Proszę spojrzeć na poniższy artykuł frederiknakstad.com/…
Ajay Beniwal
1
@MichaelCalkins samo umieszczenie linku nie jest konstruktywne. Powinieneś przynajmniej powiedzieć, co ma dostarczyć link.
Dave Gordon
My b: AngularJS Access Control and Authentication coderwall.com/p/f6brkg
Michael J. Calkins
Zespół OAuth ma świetną bibliotekę do tego andreareginato.github.io/oauth-ng
Faktor 10

Odpowiedzi:

48

Utworzyłem repozytorium github podsumowujące zasadniczo ten artykuł: https://medium.com/opinionated-angularjs/techniques-for-authentication-in-angularjs-applications-7bbf0346acec

ng-login repozytorium Github

Plunker

Postaram się wyjaśnić jak najlepiej, mam nadzieję, że pomogę niektórym z was:

(1) app.js: Tworzenie stałych uwierzytelniania w definicji aplikacji

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) Usługa autoryzacji: Wszystkie poniższe funkcje są zaimplementowane w usłudze auth.js. Usługa $ http służy do komunikacji z serwerem w celu przeprowadzenia procedur uwierzytelniania. Zawiera również funkcje dotyczące autoryzacji, czyli czy użytkownik może wykonać określoną akcję.

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) Sesja: singleton do przechowywania danych użytkownika. Wdrożenie tutaj zależy od Ciebie.

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) Kontroler nadrzędny: Potraktuj to jako „główną” funkcję aplikacji, wszystkie kontrolery dziedziczą po tym kontrolerze i jest to podstawa uwierzytelniania tej aplikacji.

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

(5) Kontrola dostępu: Aby odmówić dostępu na niektórych trasach, należy wdrożyć 2 kroki:

a) Dodaj dane o rolach, które mają dostęp do każdej trasy, w usłudze $ stateProvider routera ui, jak widać poniżej (to samo może działać dla ngRoute).

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

b) W $ rootScope. $ on ('$ stateChangeStart') dodaj funkcję, aby zapobiec zmianie stanu, jeśli użytkownik nie jest autoryzowany.

$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) Przechwytywacz uwierzytelniania: jest zaimplementowany, ale nie można go sprawdzić w zakresie tego kodu. Po każdym żądaniu $ http ten przechwytywacz sprawdza kod stanu, jeśli zostanie zwrócony jeden z poniższych, rozgłasza zdarzenie, aby zmusić użytkownika do ponownego zalogowania.

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 Błąd z autouzupełnianiem danych formularza, jak podano w pierwszym artykule, można łatwo uniknąć, dodając dyrektywę zawartą w directives.js.

PS2 Ten kod może być łatwo modyfikowany przez użytkownika, aby umożliwić oglądanie różnych tras lub wyświetlanie treści, które nie miały być wyświetlane. Logika MUSI być zaimplementowana po stronie serwera, to tylko sposób na poprawne pokazanie rzeczy w Twojej aplikacji ng-app.

Alex Arvanitidis
źródło
1
Podążałem za twoim przewodnikiem, aby skupić się na logice po stronie klienta. To jest naprawdę dobre!! Brakowało mi czegoś na temat ręcznego niszczenia sesji, ale musimy też eksperymentować i niszczyć rzeczy!
Sebastialonso
~~ nie jestem pewien, czy dobrze rozumiem tę linię: authService.login() = [...]te nawiasy kwadratowe będą oznaczać coś takiego $http.get(url, {uID, pwd}? ~~ ok, zajrzałem do plunkera, było tak, jak powiedziałem XD
netalex
1
czy możesz rozszerzyć swoją odpowiedź na stronę serwera?
zapytanie
25

Podoba mi się to podejście i zaimplementowałem je po stronie serwera bez wykonywania jakichkolwiek czynności związanych z uwierzytelnianiem na front-endzie

Moja „technika” w mojej najnowszej aplikacji to… klient nie dba o Auth. Każda rzecz w aplikacji wymaga najpierw zalogowania się, więc serwer zawsze obsługuje stronę logowania, chyba że w sesji zostanie wykryty istniejący użytkownik. Jeśli zostanie znaleziony session.user, serwer wysyła po prostu plik index.html. Bam: -o

Poszukaj komentarza „Andrew Joslina”.

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

marzyciel
źródło
3
jeśli jest to internetowy interfejs API? Zgaduję, że nie otrzymałem odpowiedzi :(
Leandro De Mello Fagundes,
1
Co jeśli chcesz wyświetlić nazwę użytkownika? Lub jeśli rozmawiasz z usługą, której nazwa użytkownika znajduje się w adresach URL punktów końcowych?
perrygeo
2
przepraszam, ale nie rozumiem odpowiedzi. jak radzisz sobie z sesją w trybie angular? gdzie jest session.user ustawiony? czy mógłbyś zrobić przykład kodu tego? dziękuję
François Romain
4
Sesje są obsługiwane po stronie klienta, a nie serwera, klient zapisuje token i wysyła go jako część każdego żądania, które wysyła. Serwer weryfikuje token i przetwarza żądanie
daydreamer
4
Czy ktoś, kto to rozumie, mógłby edytować tę odpowiedź dla reszty z nas?
Alojz Janez
14

Odpowiedziałem tutaj na podobne pytanie: AngularJS Authentication + RESTful API


Napisałem moduł AngularJS dla UserApp, który obsługuje chronione / publiczne trasy, przekierowywanie przy logowaniu / wylogowywaniu, bicie serca do sprawdzania statusu, przechowuje token sesji w pliku cookie, zdarzenia itp.

Możesz:

  1. Zmodyfikuj moduł i dołącz go do własnego API lub
  2. Używaj modułu razem z UserApp ( oparty na chmurze interfejs API do zarządzania użytkownikami)

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

Jeśli używasz UserApp, nie będziesz musiał pisać żadnego kodu po stronie serwera dla danych użytkownika (więcej niż walidacja tokena). Weź udział w kursie Codecademy, aby go wypróbować.

Oto kilka przykładów tego, jak to działa:

  • Jak określić, które trasy mają być publiczne, a która trasa to formularz logowania:

    $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'});
    

    .otherwise()Trasa powinna być ustawiona na którym użytkownicy mają zostać przekierowane po zalogowaniu. Przykład:

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

  • Formularz logowania z obsługą błędów:

    <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>
    
  • Formularz rejestracyjny z obsługą błędów:

    <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 do wylogowania:

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

    (Kończy sesję i przekierowuje do trasy logowania)

  • Dostęp do właściwości użytkownika:

    Dostęp do właściwości użytkownika uzyskuje się za pośrednictwem userusługi, np .:user.current.email

    Lub w szablonie: <span>{{ user.email }}</span>

  • Ukryj elementy, które powinny być widoczne tylko po zalogowaniu:

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

  • Pokaż element na podstawie uprawnień:

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

Aby uwierzytelnić się w usługach zaplecza, wystarczy user.token()pobrać token sesji i wysłać go z żądaniem AJAX. Na zapleczu użyj interfejsu API UserApp (jeśli używasz aplikacji UserApp), aby sprawdzić, czy token jest ważny, czy nie.

Jeśli potrzebujesz pomocy, daj mi znać!

Timothy E. Johansson
źródło
W jaki sposób „Zmodyfikuj moduł i dołącz go do własnego interfejsu API” ?
Pureferret
2

W angularjs możesz utworzyć część interfejsu użytkownika, usługę, dyrektywy i całą część angularjs, która reprezentuje interfejs użytkownika. To fajna technologia do pracy.

Jak każdy, kto jest nowy w tej technologii i chce uwierzytelnić „użytkownika”, sugeruję, aby zrobić to z mocą C # Web API. w tym celu możesz skorzystać ze specyfikacji OAuth, która pomoże Ci zbudować silny mechanizm bezpieczeństwa do uwierzytelniania użytkownika. po zbudowaniu WebApi z OAuth musisz wywołać ten interfejs API dla tokena:

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;
 
    };
 

a gdy otrzymasz token, zażądasz zasobów od angularjs za pomocą tokena i uzyskasz dostęp do zasobu, który jest bezpieczny w interfejsie API sieciowym ze specyfikacją OAuth.

Aby uzyskać dodatkową pomoc, zapoznaj się z poniższym artykułem: -

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

Gurupreet
źródło
1

Myślę, że każda odpowiedź JSON powinna zawierać właściwość (np. {Uwierzytelniony: fałsz}), a klient za każdym razem musi ją przetestować: jeśli jest fałszywa, to kontroler / usługa Angular "przekieruje" na stronę logowania.

A co się stanie, jeśli użytkownik złapie de JSON i zmieni wartość bool na True?

Myślę, że nigdy nie powinieneś polegać na kliencie przy tego typu sprawach. Jeśli użytkownik nie jest uwierzytelniony, serwer powinien po prostu przekierować do strony logowania / błędu.

Doum
źródło
2
Sprawdź to: github.com/witoldsz/angular-http-auth - przechwytywacz sprawdza kod statusu odpowiedzi serwera i jeśli jest to 403 ('wymagane logowanie'), transmituje zdarzenie, więc możesz go przechwycić w aplikacji i wyświetlić pole logowania.
aherok
10
Przestańcie sobie odpowiadać, używając odpowiedzi. Po to są komentarze!
Soviut
@aherok sugestia, Twój komentarz powinien zostać oznaczony jako odpowiedź, zostanie on w odpowiednim czasie wybrany na szczyt. reszta to tylko hałas.
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
źródło