Otentikasi login ui-router AngularJS

376

Saya baru mengenal AngularJS, dan saya sedikit bingung bagaimana saya bisa menggunakan angular- "ui-router" dalam skenario berikut:

Saya sedang membangun aplikasi web yang terdiri dari dua bagian. Bagian pertama adalah beranda dengan tampilan masuk dan pendaftarannya, dan bagian kedua adalah dasbor (setelah berhasil masuk).

Saya telah membuat bagian index.htmluntuk rumah dengan aplikasi bersudut dan ui-routerkonfigurasi untuk menangani /logindan /signupmelihat, dan ada file lain dashboard.htmluntuk bagian dasbor dengan aplikasi danui-router konfigurasi untuk menangani banyak tampilan sub.

Sekarang saya menyelesaikan bagian dasbor dan tidak tahu bagaimana menggabungkan dua bagian dengan aplikasi sudut yang berbeda. Bagaimana saya bisa memberi tahu aplikasi rumah untuk mengalihkan ke aplikasi dasbor?

Ahmed Hashem
sumber
1
Bisakah Anda berbagi beberapa kode dengan kami?
Chancho
6
@ Chancho Saya pikir ini bukan tentang kode, sungguh saya tidak tahu kode apa yang harus saya bagikan.
Ahmed Hashem
ya, tolong bagikan kodenya, pertanyaan yang sangat umum ...
Alireza

Jawaban:

607

Saya sedang dalam proses membuat demo yang lebih bagus serta membersihkan beberapa layanan ini menjadi modul yang dapat digunakan, tapi inilah yang saya buat. Ini adalah proses kompleks untuk mengatasi beberapa peringatan, jadi bertahanlah di sana. Anda harus memecah ini menjadi beberapa bagian.

Lihatlah bagian ini .

Pertama, Anda memerlukan layanan untuk menyimpan identitas pengguna. Saya menyebutnya principal. Itu dapat diperiksa untuk melihat apakah pengguna login, dan atas permintaan, itu dapat menyelesaikan objek yang mewakili informasi penting tentang identitas pengguna. Ini bisa berupa apa pun yang Anda butuhkan, tetapi yang terpenting adalah nama tampilan, nama pengguna, mungkin email, dan peran yang dimiliki pengguna (jika ini berlaku untuk aplikasi Anda). Kepala sekolah juga memiliki metode untuk melakukan pengecekan peran.

.factory('principal', ['$q', '$http', '$timeout',
  function($q, $http, $timeout) {
    var _identity = undefined,
      _authenticated = false;

    return {
      isIdentityResolved: function() {
        return angular.isDefined(_identity);
      },
      isAuthenticated: function() {
        return _authenticated;
      },
      isInRole: function(role) {
        if (!_authenticated || !_identity.roles) return false;

        return _identity.roles.indexOf(role) != -1;
      },
      isInAnyRole: function(roles) {
        if (!_authenticated || !_identity.roles) return false;

        for (var i = 0; i < roles.length; i++) {
          if (this.isInRole(roles[i])) return true;
        }

        return false;
      },
      authenticate: function(identity) {
        _identity = identity;
        _authenticated = identity != null;
      },
      identity: function(force) {
        var deferred = $q.defer();

        if (force === true) _identity = undefined;

        // check and see if we have retrieved the 
        // identity data from the server. if we have, 
        // reuse it by immediately resolving
        if (angular.isDefined(_identity)) {
          deferred.resolve(_identity);

          return deferred.promise;
        }

        // otherwise, retrieve the identity data from the
        // server, update the identity object, and then 
        // resolve.
        //           $http.get('/svc/account/identity', 
        //                     { ignoreErrors: true })
        //                .success(function(data) {
        //                    _identity = data;
        //                    _authenticated = true;
        //                    deferred.resolve(_identity);
        //                })
        //                .error(function () {
        //                    _identity = null;
        //                    _authenticated = false;
        //                    deferred.resolve(_identity);
        //                });

        // for the sake of the demo, fake the lookup
        // by using a timeout to create a valid
        // fake identity. in reality,  you'll want 
        // something more like the $http request
        // commented out above. in this example, we fake 
        // looking up to find the user is
        // not logged in
        var self = this;
        $timeout(function() {
          self.authenticate(null);
          deferred.resolve(_identity);
        }, 1000);

        return deferred.promise;
      }
    };
  }
])

Kedua, Anda memerlukan layanan yang memeriksa keadaan yang ingin dituju pengguna, memastikan mereka masuk (jika perlu; tidak perlu untuk masuk, mengatur ulang kata sandi, dll.), Lalu melakukan pengecekan peran (jika aplikasi Anda butuh ini). Jika tidak diautentikasi, kirim ke halaman masuk. Jika mereka diautentikasi, tetapi gagal pemeriksaan peran, kirim mereka ke halaman akses ditolak. Saya memanggil layanan ini authorization.

.factory('authorization', ['$rootScope', '$state', 'principal',
  function($rootScope, $state, principal) {
    return {
      authorize: function() {
        return principal.identity()
          .then(function() {
            var isAuthenticated = principal.isAuthenticated();

            if ($rootScope.toState.data.roles
                && $rootScope.toState
                             .data.roles.length > 0 
                && !principal.isInAnyRole(
                   $rootScope.toState.data.roles))
            {
              if (isAuthenticated) {
                  // user is signed in but not
                  // authorized for desired state
                  $state.go('accessdenied');
              } else {
                // user is not authenticated. Stow
                // the state they wanted before you
                // send them to the sign-in state, so
                // you can return them when you're done
                $rootScope.returnToState
                    = $rootScope.toState;
                $rootScope.returnToStateParams
                    = $rootScope.toStateParams;

                // now, send them to the signin state
                // so they can log in
                $state.go('signin');
              }
            }
          });
      }
    };
  }
])

Sekarang semua yang perlu Anda lakukan yaitu mendengarkan pada ui-router's $stateChangeStart. Ini memberi Anda kesempatan untuk memeriksa keadaan saat ini, keadaan yang ingin mereka tuju, dan memasukkan cek otorisasi Anda. Jika gagal, Anda dapat membatalkan transisi rute, atau mengubah ke rute lain.

.run(['$rootScope', '$state', '$stateParams', 
      'authorization', 'principal',
    function($rootScope, $state, $stateParams, 
             authorization, principal)
{
      $rootScope.$on('$stateChangeStart', 
          function(event, toState, toStateParams)
      {
        // track the state the user wants to go to; 
        // authorization service needs this
        $rootScope.toState = toState;
        $rootScope.toStateParams = toStateParams;
        // if the principal is resolved, do an 
        // authorization check immediately. otherwise,
        // it'll be done when the state it resolved.
        if (principal.isIdentityResolved()) 
            authorization.authorize();
      });
    }
  ]);

Bagian yang sulit tentang melacak identitas pengguna adalah mencarinya jika Anda sudah mengautentikasi (katakanlah, Anda mengunjungi halaman setelah sesi sebelumnya, dan menyimpan token autentikasi dalam cookie, atau mungkin Anda sulit menyegarkan halaman, atau dijatuhkan ke URL dari tautan). Karena cara ui-routerkerjanya, Anda perlu menyelesaikan identitas Anda satu kali, sebelum pemeriksaan auth. Anda dapat melakukan ini menggunakan resolveopsi di konfigurasi keadaan Anda. Saya memiliki satu negara induk untuk situs yang mewarisi semua negara bagian, yang memaksa kepala sekolah untuk diselesaikan sebelum hal lain terjadi.

$stateProvider.state('site', {
  'abstract': true,
  resolve: {
    authorize: ['authorization',
      function(authorization) {
        return authorization.authorize();
      }
    ]
  },
  template: '<div ui-view />'
})

Ada masalah lain di sini ... resolvehanya dipanggil sekali. Setelah janji Anda untuk pencarian identitas selesai, itu tidak akan menjalankan delegasi ketetapan lagi. Jadi kami harus melakukan pemeriksaan autentikasi Anda di dua tempat: satu kali sesuai dengan janji identitas Anda yang diselesaikan, yang mencakup resolvepertama kali aplikasi Anda dimuat, dan satu kali lagi $stateChangeStartjika resolusi telah dilakukan, yang mencakup setiap saat Anda menavigasi ke seluruh negara bagian.

OK, jadi apa yang telah kita lakukan sejauh ini?

  1. Kami memeriksa untuk melihat kapan aplikasi dimuat jika pengguna masuk.
  2. Kami melacak info tentang pengguna yang masuk.
  3. Kami mengarahkan mereka untuk masuk ke negara bagian yang mengharuskan pengguna untuk masuk.
  4. Kami mengarahkan mereka ke status akses yang ditolak jika mereka tidak memiliki izin untuk mengaksesnya.
  5. Kami memiliki mekanisme untuk mengarahkan pengguna kembali ke keadaan semula yang mereka minta, jika kami membutuhkan mereka untuk masuk.
  6. Kami dapat mengeluarkan pengguna (perlu terhubung dengan klien atau kode server apa pun yang mengelola tiket auth) Anda.
  7. Kami tidak perlu mengirim pengguna kembali ke halaman masuk setiap kali mereka memuat ulang browser mereka atau menjatuhkan tautan.

Kemana kita pergi dari sini? Nah, Anda dapat mengatur negara Anda ke daerah yang membutuhkan tanda. Anda dapat meminta dikonfirmasi / pengguna yang berwenang dengan menambahkan datadengan roleske negara-negara ini (atau orang tua dari mereka, jika Anda ingin menggunakan warisan). Di sini, kami membatasi sumber daya untuk Admin:

.state('restricted', {
    parent: 'site',
    url: '/restricted',
    data: {
      roles: ['Admin']
    },
    views: {
      'content@': {
        templateUrl: 'restricted.html'
      }
    }
  })

Sekarang Anda dapat mengontrol negara-oleh-negara apa yang pengguna dapat mengakses rute. Ada masalah lain? Mungkin hanya memvariasikan sebagian dari tampilan berdasarkan apakah mereka masuk atau tidak? Tidak masalah. Gunakan principal.isAuthenticated()atau bahkanprincipal.isInRole() dengan salah satu dari banyak cara Anda dapat menampilkan suatu template atau elemen secara kondisional.

Pertama, menyuntikkan principalke controller atau apa pun, dan menempelkannya ke ruang lingkup sehingga Anda dapat menggunakannya dengan mudah dalam pandangan Anda:

.scope('HomeCtrl', ['$scope', 'principal', 
    function($scope, principal)
{
  $scope.principal = principal;
});

Tampilkan atau sembunyikan elemen:

<div ng-show="principal.isAuthenticated()">
   I'm logged in
</div>
<div ng-hide="principal.isAuthenticated()">
  I'm not logged in
</div>

Dll, begitu seterusnya, sebagainya. Bagaimanapun, dalam contoh aplikasi Anda, Anda akan memiliki keadaan untuk beranda yang akan membiarkan pengguna yang tidak diauthentikasi mampir. Mereka dapat memiliki tautan ke status masuk atau mendaftar, atau memiliki formulir yang dimasukkan ke dalam halaman itu. Apapun yang cocok untukmu.

Halaman dasbor semua bisa mewarisi dari keadaan yang mengharuskan pengguna untuk masuk dan, katakanlah, menjadi anggota Userperan. Semua hal otorisasi yang telah kita diskusikan akan mengalir dari sana.

moribvndvs
sumber
28
Terima kasih, ini sangat membantu saya untuk mendapatkan kode sendiri. Sebagai catatan, jika Anda mendapatkan loop perutean tanpa batas (bug Router UI), coba $location.pathalih-alih $state.go.
jvannistelrooy
2
Ini adalah jawaban yang bagus dan sangat membantu saya. Ketika saya mengeset user = principal di controller saya dan mencoba memanggil say user.identity (). Name dalam pandangan saya untuk mendapatkan nama pengguna yang saat ini login, saya hanya mendapatkan objek janji {then: fn, catch: fn, akhirnya :} dikembalikan dan bukan objek _identity aktual. Jika saya menggunakan user.identity.then (fn (user)) saya bisa mendapatkan objek pengguna tetapi ini sepertinya banyak kode untuk tampilan apakah saya kehilangan sesuatu?
Tandai
4
@ Ir1sh Saya akan menyelesaikan identitas pertama di controller dan menetapkannya $scope.userdi thenfungsi Anda . Anda masih dapat referensi userdalam pandangan Anda; ketika itu diselesaikan, tampilan akan diperbarui.
moribvndvs
2
@HackedByChinese Saya pikir demo Anda tidak lagi berfungsi.
Blowsie
7
@ jvannistelrooy Saya punya masalah dengan pergi () ke, tapi setelah meletakkannya di dalam kemudian setelah memanggil fungsi noop seperti ini $q.when(angular.noop).then(function(){$state.go('myState'), semuanya berfungsi seperti yang diharapkan. Jika saya menelepon $state.gosaat transisi negara lain tidak selesai, maka itu tidak akan berfungsi (saya pikir itulah alasan mengapa itu tidak akan berhasil).
Sebastian
120

Solusi yang diposting sejauh ini tidak perlu rumit, menurut saya. Ada cara yang lebih sederhana. The dokumentasiui-router mengatakan mendengarkan $locationChangeSuccessdan penggunaan$urlRouter.sync() untuk memeriksa transisi negara, menghentikan, atau melanjutkan itu. Tetapi bahkan itu sebenarnya tidak berhasil.

Namun, berikut adalah dua alternatif sederhana. Pilih salah satu:

Solusi 1: mendengarkan $locationChangeSuccess

Anda dapat mendengarkan $locationChangeSuccessdan Anda dapat melakukan beberapa logika, bahkan logika asinkron di sana. Berdasarkan logika itu, Anda dapat membiarkan fungsi kembali tidak terdefinisi, yang akan menyebabkan transisi keadaan berlanjut seperti biasa, atau Anda dapat melakukannya $state.go('logInPage'), jika pengguna perlu diautentikasi. Ini sebuah contoh:

angular.module('App', ['ui.router'])

// In the run phase of your Angular application  
.run(function($rootScope, user, $state) {

  // Listen to '$locationChangeSuccess', not '$stateChangeStart'
  $rootScope.$on('$locationChangeSuccess', function() {
    user
      .logIn()
      .catch(function() {
        // log-in promise failed. Redirect to log-in page.
        $state.go('logInPage')
      })
  })
})

Perlu diingat bahwa ini tidak benar-benar mencegah negara target memuat, tetapi itu mengarahkan ulang ke halaman login jika pengguna tidak sah. Tidak apa-apa karena sebenarnya perlindungan ada di server.

Solusi 2: menggunakan status resolve

Dalam solusi ini, Anda menggunakan ui-routerfitur tekad .

Anda pada dasarnya menolak janji itu resolvejika pengguna tidak diautentikasi dan kemudian mengarahkannya ke halaman login.

Begini caranya:

angular.module('App', ['ui.router'])

.config(
  function($stateProvider) {
    $stateProvider
      .state('logInPage', {
        url: '/logInPage',
        templateUrl: 'sections/logInPage.html',
        controller: 'logInPageCtrl',
      })
      .state('myProtectedContent', {
        url: '/myProtectedContent',
        templateUrl: 'sections/myProtectedContent.html',
        controller: 'myProtectedContentCtrl',
        resolve: { authenticate: authenticate }
      })
      .state('alsoProtectedContent', {
        url: '/alsoProtectedContent',
        templateUrl: 'sections/alsoProtectedContent.html',
        controller: 'alsoProtectedContentCtrl',
        resolve: { authenticate: authenticate }
      })

    function authenticate($q, user, $state, $timeout) {
      if (user.isAuthenticated()) {
        // Resolve the promise successfully
        return $q.when()
      } else {
        // The next bit of code is asynchronously tricky.

        $timeout(function() {
          // This code runs after the authentication promise has been rejected.
          // Go to the log-in page
          $state.go('logInPage')
        })

        // Reject the authentication promise to prevent the state from loading
        return $q.reject()
      }
    }
  }
)

Tidak seperti solusi pertama, solusi ini sebenarnya mencegah negara target dari memuat.

MK Safi
sumber
6
@ FredLackey mengatakan pengguna yang tidak diautentikasi ada di state A. Mereka mengklik tautan untuk menuju protected state Btetapi Anda ingin mengarahkan mereka kembali logInPage. Jika tidak ada $timeout, ui-routerhanya akan menghentikan semua transisi negara, sehingga pengguna akan terjebak state A. The $timeoutmemungkinkan ui-routeruntuk pertama mencegah transisi awal untuk protected state Bkarena tekad itu ditolak dan setelah itu selesai, itu diarahkan ke logInPage.
MK Safi
Di mana authenticatefungsi sebenarnya disebut?
CodyBugstein
authenticateFungsi @Imray diteruskan sebagai parameter untuk ui-router. Anda tidak harus menyebutnya sendiri. ui-routersebut itu.
MK Safi
Mengapa Anda menggunakan '$ locationChangeSuccess' alih-alih '$ stateChangeStart'?
Draex_
@ PeterDraexDräxler Saya kebanyakan mengikuti dokumentasi. Apakah Anda melihat ada perbedaan dengan menggunakan $stateChangeStart?
MK Safi
42

Solusi termudah adalah dengan menggunakan $stateChangeStartdan event.preventDefault()untuk membatalkan perubahan negara ketika pengguna tidak dikonfirmasi dan mengarahkan dia ke auth negara yang halaman login.

angular
  .module('myApp', [
    'ui.router',
  ])
    .run(['$rootScope', 'User', '$state',
    function ($rootScope, User, $state) {
      $rootScope.$on('$stateChangeStart', function (event, toState, toParams, fromState, fromParams) {
        if (toState.name !== 'auth' && !User.authenticaded()) {
          event.preventDefault();
          $state.go('auth');
        }
      });
    }]
  );
sebest
sumber
6
Saya tidak berpikir ini akan berfungsi jika User.authenticaded () adalah panggilan async. Itu adalah cawan suci yang semua orang kejar. Misalnya, jika setiap negara kecuali "login" diamankan, saya ingin mengonfirmasi pengguna masih diautentikasi SEBELUM memuat keadaan apa pun. Menggunakan resolves menyebalkan karena mereka hanya menyelesaikan satu kali, dan untuk mencegah status anak tidak dimuat, Anda harus menyuntikkan resolusinya ke SETIAP ANAK .
Jason
terotentikasi bukan panggilan async dalam kasus saya: `this.authenticaded = function () {if (this.currentAccountID! == null) {return true; } return false; }; `
sebest
Per: stackoverflow.com/a/38374313/849829 , 'run' berada jauh di atas 'layanan dan karenanya masalahnya. Memeriksa penyimpanan lokal untuk status yang diautentikasi tampaknya merupakan pendekatan yang baik.
Deepak Thomas
22

Saya pikir Anda memerlukan serviceyang menangani proses otentikasi (dan penyimpanannya).

Dalam layanan ini Anda memerlukan beberapa metode dasar:

  • isAuthenticated()
  • login()
  • logout()
  • dll ...

Layanan ini harus disuntikkan di controller Anda masing-masing modul:

  • Di bagian dasbor Anda, gunakan layanan ini untuk memeriksa apakah pengguna diautentikasi (service.isAuthenticated() metode). jika tidak, arahkan ke / login
  • Di bagian login Anda, cukup gunakan formulir data untuk mengotentikasi pengguna melalui Anda service.login() metode

Contoh yang baik dan kuat untuk perilaku ini adalah aplikasi sudut- proyek dan khususnya modul keamanannya yang didasarkan pada Modul Interceptor Auth HTTP yang mengagumkan

Semoga ini membantu

Cétia
sumber
21

Saya Membuat modul ini untuk membantu membuat sepotong kue proses ini

Anda dapat melakukan hal-hal seperti:

$routeProvider
  .state('secret',
    {
      ...
      permissions: {
        only: ['admin', 'god']
      }
    });

Atau juga

$routeProvider
  .state('userpanel',
    {
      ...
      permissions: {
        except: ['not-logged-in']
      }
    });

Ini baru tapi layak untuk dicoba!

https://github.com/Narzerus/angular-permission

Rafael Vidaurre
sumber
2
apa yang harus saya hentikan mengedit sumber saat runtime dan menghapus 'admin' Anda || 'tuhan' dan berlanjut?
Pogrindis
12
Saya berharap bahwa setiap permintaan data yang memerlukan otorisasi juga sedang diverifikasi di server.
Ben Ripley
24
Ini tidak dimaksudkan untuk keamanan, otorisasi sisi klien tidak pernah ada karena Anda selalu dapat mengubah nilai. Anda bahkan dapat mencegat tanggapan dari sisi server dan mengevaluasinya sebagai "resmi". Maksud dari izin / otorisasi di sisi klien adalah untuk menghindari membiarkan pengguna melakukan hal-hal terlarang untuk tujuan ux. Misalnya jika Anda menangani tindakan khusus admin, bahkan jika pengguna dengan licik menipu klien untuk mengizinkan pengiriman permintaan terbatas ke server, server masih akan mengembalikan respons 401. Ini tentu saja selalu merupakan tanggung jawab api yang sedang diterapkan @BenRipley
Rafael Vidaurre
3
Respons yang bagus untuk pertanyaan Rafael. Selalu lindungi api karena front-end adalah hal yang paling dapat direkayasa dan dipalsukan.
Frankie Loscavio
1
Masalah dengan sejarah ini sudah dipecahkan untuk sementara waktu sekarang @Bohdan. Anda dapat menggunakannya dengan aman bahkan dengan ui-router ekstra.
masterspambot
16

Saya ingin berbagi solusi lain yang bekerja dengan router ui 1.0.0.X

Seperti yang Anda ketahui, stateChangeStart dan stateChangeSuccess sekarang tidak digunakan lagi. https://github.com/angular-ui/ui-router/issues/2655

Sebagai gantinya Anda harus menggunakan $ transisi http://angular-ui.github.io/ui-router/1.0.0-alpha.1/interfaces/transition.ihookregistry.html

Beginilah cara saya mencapainya:

Pertama saya miliki dan AuthService dengan beberapa fungsi yang bermanfaat

angular.module('myApp')

        .factory('AuthService',
                ['$http', '$cookies', '$rootScope',
                    function ($http, $cookies, $rootScope) {
                        var service = {};

                        // Authenticates throug a rest service
                        service.authenticate = function (username, password, callback) {

                            $http.post('api/login', {username: username, password: password})
                                    .success(function (response) {
                                        callback(response);
                                    });
                        };

                        // Creates a cookie and set the Authorization header
                        service.setCredentials = function (response) {
                            $rootScope.globals = response.token;

                            $http.defaults.headers.common['Authorization'] = 'Bearer ' + response.token;
                            $cookies.put('globals', $rootScope.globals);
                        };

                        // Checks if it's authenticated
                        service.isAuthenticated = function() {
                            return !($cookies.get('globals') === undefined);
                        };

                        // Clear credentials when logout
                        service.clearCredentials = function () {
                            $rootScope.globals = undefined;
                            $cookies.remove('globals');
                            $http.defaults.headers.common.Authorization = 'Bearer ';
                        };

                        return service;
                    }]);

Maka saya memiliki konfigurasi ini:

angular.module('myApp', [
    'ui.router',
    'ngCookies'
])
        .config(['$stateProvider', '$urlRouterProvider',
            function ($stateProvider, $urlRouterProvider) {
                $urlRouterProvider.otherwise('/resumen');
                $stateProvider
                        .state("dashboard", {
                            url: "/dashboard",
                            templateUrl: "partials/dashboard.html",
                            controller: "dashCtrl",
                            data: {
                                authRequired: true
                            }
                        })
                        .state("login", {
                            url: "/login",
                            templateUrl: "partials/login.html",
                            controller: "loginController"
                        })
            }])

        .run(['$rootScope', '$transitions', '$state', '$cookies', '$http', 'AuthService',
            function ($rootScope, $transitions, $state, $cookies, $http, AuthService) {

                // keep user logged in after page refresh
                $rootScope.globals = $cookies.get('globals') || {};
                $http.defaults.headers.common['Authorization'] = 'Bearer ' + $rootScope.globals;

                $transitions.onStart({
                    to: function (state) {
                        return state.data != null && state.data.authRequired === true;
                    }
                }, function () {
                    if (!AuthService.isAuthenticated()) {
                        return $state.target("login");
                    }
                });
            }]);

Anda dapat melihat bahwa saya menggunakan

data: {
   authRequired: true
}

untuk menandai negara hanya dapat diakses jika disahkan.

kemudian, pada .run saya menggunakan transisi untuk memeriksa keadaan diautentikasi

$transitions.onStart({
    to: function (state) {
        return state.data != null && state.data.authRequired === true;
    }
}, function () {
    if (!AuthService.isAuthenticated()) {
        return $state.target("login");
    }
});

Saya membuat contoh ini menggunakan beberapa kode yang ditemukan pada dokumentasi $ transisi. Saya cukup baru dengan router ui tetapi berfungsi.

Semoga bisa membantu siapa saja.

Sergio Fernandez
sumber
Ini bagus untuk mereka yang menggunakan router yang lebih baru. Terima kasih!
mtro
5

Berikut adalah cara kami keluar dari perutean rute tak terbatas dan masih digunakan $state.gosebagai ganti$location.path

if('401' !== toState.name) {
  if (principal.isIdentityResolved()) authorization.authorize();
}
Jason Girdner
sumber
1
Adakah yang tahu mengapa ketika menggunakan jawaban / pengaturan yang diterima yang dijelaskan di atas bilah alamat tidak lagi menampilkan url dan semua fragmen dan params string kueri? Sejak menerapkan ini, bilah alamat tidak lagi memungkinkan aplikasi kami untuk ditandai.
Frankie Loscavio
1
Bukankah ini seharusnya menjadi komentar pada salah satu jawaban yang ada? Karena tidak ada kode seperti itu di OP dan bahkan tidak jelas jawaban apa / kode apa ini merujuk
TJ
3

Saya punya solusi lain: solusi itu bekerja dengan sempurna ketika Anda hanya memiliki konten yang ingin Anda tampilkan ketika Anda login. Tetapkan aturan di mana Anda memeriksa apakah Anda login dan bukan jalur rute daftar putih.

$urlRouterProvider.rule(function ($injector, $location) {
   var UserService = $injector.get('UserService');
   var path = $location.path(), normalized = path.toLowerCase();

   if (!UserService.isLoggedIn() && path.indexOf('login') === -1) {
     $location.path('/login/signin');
   }
});

Dalam contoh saya, saya bertanya apakah saya tidak masuk dan rute saat ini yang saya ingin rute bukan bagian dari `/ login ', karena rute daftar putih saya adalah sebagai berikut

/login/signup // registering new user
/login/signin // login to app

jadi saya punya akses instan ke dua rute ini dan setiap rute lainnya akan diperiksa jika Anda online.

Ini seluruh file perutean saya untuk modul login

export default (
  $stateProvider,
  $locationProvider,
  $urlRouterProvider
) => {

  $stateProvider.state('login', {
    parent: 'app',
    url: '/login',
    abstract: true,
    template: '<ui-view></ui-view>'
  })

  $stateProvider.state('signin', {
    parent: 'login',
    url: '/signin',
    template: '<login-signin-directive></login-signin-directive>'
  });

  $stateProvider.state('lock', {
    parent: 'login',
    url: '/lock',
    template: '<login-lock-directive></login-lock-directive>'
  });

  $stateProvider.state('signup', {
    parent: 'login',
    url: '/signup',
    template: '<login-signup-directive></login-signup-directive>'
  });

  $urlRouterProvider.rule(function ($injector, $location) {
    var UserService = $injector.get('UserService');
    var path = $location.path();

    if (!UserService.isLoggedIn() && path.indexOf('login') === -1) {
         $location.path('/login/signin');
    }
  });

  $urlRouterProvider.otherwise('/error/not-found');
}

() => { /* code */ } adalah sintaks ES6, gunakan sebagai gantinya function() { /* code */ }

Chris Incoqnito
sumber
3

Gunakan $ http Interceptor

Dengan menggunakan interseptor $ http, Anda dapat mengirim header ke Back-end atau sebaliknya dan melakukan pemeriksaan dengan cara itu.

Artikel bagus tentang pencegat $ http

Contoh:

$httpProvider.interceptors.push(function ($q) {
        return {
            'response': function (response) {

                // TODO Create check for user authentication. With every request send "headers" or do some other check
                return response;
            },
            'responseError': function (reject) {

                // Forbidden
                if(reject.status == 403) {
                    console.log('This page is forbidden.');
                    window.location = '/';
                // Unauthorized
                } else if(reject.status == 401) {
                    console.log("You're not authorized to view this page.");
                    window.location = '/';
                }

                return $q.reject(reject);
            }
        };
    });

Masukkan ini ke dalam fungsi .config atau .run Anda.

TSlegaitis
sumber
2

Pertama, Anda akan memerlukan layanan yang dapat disuntikkan ke pengontrol Anda yang memiliki beberapa gagasan tentang status otentikasi aplikasi. Rincian auth yang ada dengan penyimpanan lokal adalah cara yang layak untuk mendekatinya.

Selanjutnya, Anda harus memeriksa status auth tepat sebelum status berubah. Karena aplikasi Anda memiliki beberapa halaman yang perlu diautentikasi dan yang lain tidak, buat rute induk yang memeriksa auth, dan buat semua halaman lain yang memerlukan yang sama menjadi anak dari orangtua tersebut.

Terakhir, Anda perlu beberapa cara untuk mengetahui apakah pengguna Anda yang saat ini masuk dapat melakukan operasi tertentu. Ini dapat dicapai dengan menambahkan fungsi 'dapat' ke layanan auth Anda. Dapat mengambil dua parameter: - tindakan - wajib - (yaitu 'manage_dashboards' atau 'create_new_dashboard') - objek - opsional - objek sedang dioperasikan. Misalnya, jika Anda memiliki objek dasbor, Anda mungkin ingin memeriksa untuk melihat apakah dashboard.ownerId === loginInUser.id. (Tentu saja, informasi yang dikirimkan dari klien tidak boleh dipercaya dan Anda harus selalu memverifikasi ini di server sebelum menuliskannya ke database Anda).

angular.module('myApp', ['ngStorage']).config([
   '$stateProvider',
function(
   $stateProvider
) {
   $stateProvider
     .state('home', {...}) //not authed
     .state('sign-up', {...}) //not authed
     .state('login', {...}) //not authed
     .state('authed', {...}) //authed, make all authed states children
     .state('authed.dashboard', {...})
}])
.service('context', [
   '$localStorage',
function(
   $localStorage
) {
   var _user = $localStorage.get('user');
   return {
      getUser: function() {
         return _user;
      },
      authed: function() {
         return (_user !== null);
      },
      // server should return some kind of token so the app 
      // can continue to load authenticated content without having to
      // re-authenticate each time
      login: function() {
         return $http.post('/login.json').then(function(reply) {
            if (reply.authenticated === true) {
               $localStorage.set(_userKey, reply.user);
            }
         });
      },
      // this request should expire that token, rendering it useless
      // for requests outside of this session
      logout: function() {
         return $http.post('logout.json').then(function(reply) {
            if (reply.authenticated === true) {
               $localStorage.set(_userKey, reply.user);
            }
         });
      },
      can: function(action, object) {
         if (!this.authed()) {
            return false;
         }

         var user = this.getUser();

         if (user && user.type === 'admin') {
             return true;
         }

         switch(action) {
            case 'manage_dashboards':
               return (user.type === 'manager');
         }

         return false;


      }
   }
}])
.controller('AuthCtrl', [
   'context', 
   '$scope', 
function(
   context, 
   $scope
) {
   $scope.$root.$on('$stateChangeStart', function(event, toState, toParams, fromState, fromParams) {
      //only require auth if we're moving to another authed page
      if (toState && toState.name.indexOf('authed') > -1) {
         requireAuth();
      }
   });

   function requireAuth() {
      if (!context.authed()) {
         $state.go('login');
      }
   }
}]

** PENOLAKAN: Kode di atas adalah kode semu dan tidak disertai jaminan **

colefner
sumber