Xác thực đăng nhập AngularJS ui-router


376

Tôi chưa quen với AngularJS và tôi hơi bối rối về cách tôi có thể sử dụng angular- "ui-router" trong kịch bản sau:

Tôi đang xây dựng một ứng dụng web bao gồm hai phần. Phần đầu tiên là trang chủ với các lượt xem đăng nhập và đăng ký và phần thứ hai là bảng điều khiển (sau khi đăng nhập thành công).

Tôi đã tạo một phần index.htmlcho phần nhà với ứng dụng và ui-routercấu hình góc cạnh của nó để xử lý /login/signupxem, và có một tệp khác dashboard.htmlcho phần bảng điều khiển với ứng dụng và ui-routercấu hình của nó để xử lý nhiều chế độ xem phụ.

Bây giờ tôi đã hoàn thành phần bảng điều khiển và không biết cách kết hợp hai phần với các ứng dụng góc cạnh khác nhau của chúng. Làm cách nào tôi có thể yêu cầu ứng dụng nhà chuyển hướng đến ứng dụng bảng điều khiển?


1
Bạn có thể chia sẻ một số mã với chúng tôi?
Chancho

6
@Chancho Tôi nghĩ đó không phải là về mã, thực sự tôi không biết nên chia sẻ mã nào.
Ahmed Hashem

vâng, xin vui lòng chia sẻ mã, câu hỏi rất chung chung ...
Alireza

Câu trả lời:


607

Tôi đang trong quá trình tạo bản demo đẹp hơn cũng như dọn dẹp một số dịch vụ này thành một mô-đun có thể sử dụng được, nhưng đây là những gì tôi nghĩ ra. Đây là một quá trình phức tạp để làm việc xung quanh một số hãy cẩn thận, vì vậy hãy chờ đợi ở đó. Bạn sẽ cần phải chia nó thành nhiều phần.

Hãy nhìn vào plunk này .

Đầu tiên, bạn cần một dịch vụ để lưu trữ danh tính của người dùng. Tôi gọi đây principal. Nó có thể được kiểm tra để xem người dùng đã đăng nhập chưa, và theo yêu cầu, nó có thể giải quyết một đối tượng thể hiện thông tin cần thiết về danh tính của người dùng. Đây có thể là bất cứ điều gì bạn cần, nhưng các yếu tố cần thiết sẽ là tên hiển thị, tên người dùng, có thể là email và vai trò của người dùng (nếu điều này áp dụng cho ứng dụng của bạn). Hiệu trưởng cũng có phương pháp để kiểm tra vai trò.

.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;
      }
    };
  }
])

Thứ hai, bạn cần một dịch vụ kiểm tra trạng thái mà người dùng muốn đến, đảm bảo họ đã đăng nhập (nếu cần thiết, không cần thiết để đăng nhập, đặt lại mật khẩu, v.v.), sau đó thực hiện kiểm tra vai trò (nếu ứng dụng của bạn cần cái này). Nếu chúng không được xác thực, hãy gửi chúng đến trang đăng nhập. Nếu chúng được xác thực, nhưng không kiểm tra vai trò, hãy gửi chúng đến trang bị từ chối truy cập. Tôi gọi dịch vụ này 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');
              }
            }
          });
      }
    };
  }
])

Bây giờ tất cả bạn cần làm là lắng nghe trên ui-router's $stateChangeStart. Điều này cho bạn cơ hội kiểm tra trạng thái hiện tại, trạng thái họ muốn đến và chèn kiểm tra ủy quyền của bạn. Nếu thất bại, bạn có thể hủy quá trình chuyển tuyến hoặc thay đổi tuyến khác.

.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();
      });
    }
  ]);

Phần khó khăn trong việc theo dõi danh tính của người dùng là tìm kiếm nếu bạn đã xác thực (giả sử bạn đang truy cập trang sau một phiên trước đó và lưu mã thông báo xác thực trong cookie hoặc có thể bạn đã làm mới một trang hoặc rơi vào một URL từ một liên kết). Do cách thức ui-routerhoạt động, bạn cần giải quyết danh tính của mình một lần, trước khi kiểm tra xác thực của bạn. Bạn có thể làm điều này bằng cách sử dụng resolvetùy chọn trong cấu hình trạng thái của bạn. Tôi có một trạng thái cha mẹ cho trang web mà tất cả các tiểu bang được thừa kế từ đó buộc hiệu trưởng phải được giải quyết trước khi có bất kỳ điều gì khác xảy ra.

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

Có một vấn đề khác ở đây ... resolvechỉ được gọi một lần. Khi lời hứa tìm kiếm danh tính của bạn hoàn tất, nó sẽ không chạy lại ủy nhiệm giải quyết. Vì vậy, chúng tôi phải kiểm tra xác thực của bạn ở hai nơi: một lần theo lời hứa nhận dạng của bạn, giải quyết resolvelần đầu tiên ứng dụng của bạn tải và một lần $stateChangeStartnếu giải quyết xong, bao gồm bất kỳ lúc nào bạn điều hướng quanh các tiểu bang.

OK, vậy chúng ta đã làm gì cho đến nay?

  1. Chúng tôi kiểm tra xem khi tải ứng dụng nếu người dùng đăng nhập.
  2. Chúng tôi theo dõi thông tin về người dùng đã đăng nhập.
  3. Chúng tôi chuyển hướng họ để đăng nhập vào trạng thái yêu cầu người dùng đăng nhập.
  4. Chúng tôi chuyển hướng họ đến trạng thái từ chối truy cập nếu họ không có quyền truy cập.
  5. Chúng tôi có một cơ chế để chuyển hướng người dùng trở lại trạng thái ban đầu họ yêu cầu, nếu chúng tôi cần họ đăng nhập.
  6. Chúng tôi có thể đăng xuất người dùng (cần được kết nối đồng bộ với bất kỳ mã máy khách hoặc mã máy chủ nào quản lý vé xác thực của bạn).
  7. Chúng tôi không cần phải gửi người dùng trở lại trang đăng nhập mỗi khi họ tải lại trình duyệt của họ hoặc thả vào một liên kết.

Chúng ta sẽ đi đâu từ đây? Vâng, bạn có thể tổ chức các quốc gia của bạn vào khu vực mà yêu cầu đăng nhập. Bạn có thể yêu cầu chứng thực người dùng / ủy quyền bằng cách thêm datavới rolesnhững tiểu bang (hoặc cha mẹ của họ, nếu bạn muốn sử dụng thừa kế). Ở đây, chúng tôi giới hạn tài nguyên cho Quản trị viên:

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

Bây giờ bạn có thể kiểm soát từng tiểu bang những gì người dùng có thể truy cập một tuyến đường. Bất kỳ mối quan tâm khác? Có lẽ chỉ khác nhau một phần của chế độ xem dựa trên việc họ có đăng nhập hay không? Không vấn đề gì. Sử dụng principal.isAuthenticated()hoặc thậm chí principal.isInRole()với bất kỳ cách nào trong số nhiều cách bạn có thể hiển thị một cách có điều kiện một mẫu hoặc một phần tử.

Đầu tiên, đưa principalvào bộ điều khiển hoặc bất cứ thứ gì, và gắn nó vào phạm vi để bạn có thể sử dụng nó dễ dàng trong chế độ xem của mình:

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

Hiển thị hoặc ẩn một phần tử:

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

V.v., v.v. Dù sao, trong ứng dụng ví dụ của bạn, bạn sẽ có một trạng thái cho trang chủ sẽ cho phép người dùng không được xác thực ghé qua. Họ có thể có các liên kết đến trạng thái đăng nhập hoặc đăng ký hoặc có các biểu mẫu được tích hợp trong trang đó. Bất cứ điều gì phù hợp với bạn.

Tất cả các trang bảng điều khiển đều có thể kế thừa từ trạng thái yêu cầu người dùng phải đăng nhập và, giả sử, là Userthành viên vai trò. Tất cả những thứ ủy quyền mà chúng ta đã thảo luận sẽ chảy từ đó.


28
Cảm ơn, điều này thực sự giúp tôi có được mã của riêng mình. Mặt khác, nếu bạn gặp vòng lặp định tuyến vô hạn (lỗi Bộ định tuyến UI), hãy thử $location.paththay vì $state.go.
jvannistelrooy

2
Đây là một câu trả lời tuyệt vời và nó đã giúp tôi rất nhiều. Khi tôi đặt user = vice trong bộ điều khiển của mình và cố gắng gọi tên user.identity (). Theo quan điểm của tôi để lấy tên người dùng hiện đang đăng nhập, tôi dường như chỉ nhận được đối tượng lời hứa {sau đó: fn, Catch: fn, cuối cùng :} trả về và không phải là đối tượng _identity thực tế. Nếu tôi sử dụng user.identity.then (fn (user)) tôi có thể lấy đối tượng người dùng nhưng điều này có vẻ như rất nhiều mã cho chế độ xem tôi có thiếu thứ gì không?
Đánh dấu

4
@ Ir1sh Tôi sẽ giải quyết danh tính đầu tiên trong bộ điều khiển và gán nó $scope.uservào thenchức năng của bạn . Bạn vẫn có thể tham khảo usertrong quan điểm của bạn; Khi nó được giải quyết, chế độ xem sẽ được cập nhật.
moribvndvs

2
@HackedByChinese Tôi nghĩ bản demo của bạn không còn hoạt động.
Blowsie

7
@jvannistelrooy Tôi gặp vấn đề với go (), nhưng sau khi đặt nó vào trong thì sau khi gọi hàm noop như thế này $q.when(angular.noop).then(function(){$state.go('myState'), mọi thứ hoạt động như mong đợi. Nếu tôi gọi $state.gotrong khi quá trình chuyển đổi trạng thái khác chưa hoàn thành, thì nó sẽ không hoạt động (tôi nghĩ đó là lý do tại sao nó không hoạt động).
Sebastian

120

Các giải pháp được đăng cho đến nay là phức tạp không cần thiết, theo ý kiến ​​của tôi. Có một cách đơn giản hơn. Các tài liệu vềui-router nói nghe $locationChangeSuccessvà sử dụng $urlRouter.sync()để kiểm tra một chuyển trạng thái, ngăn chặn nó, hoặc phục hồi lại. Nhưng ngay cả điều đó thực sự không hoạt động.

Tuy nhiên, đây là hai lựa chọn thay thế đơn giản. Chọn một:

Giải pháp 1: lắng nghe $locationChangeSuccess

Bạn có thể nghe $locationChangeSuccessvà bạn có thể thực hiện một số logic, thậm chí logic không đồng bộ ở đó. Dựa trên logic đó, bạn có thể để hàm trả về không xác định, điều này sẽ khiến quá trình chuyển đổi trạng thái tiếp tục như bình thường hoặc bạn có thể làm $state.go('logInPage'), nếu người dùng cần được xác thực. Đây là một ví dụ:

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

Hãy nhớ rằng điều này không thực sự ngăn trạng thái mục tiêu tải, nhưng nó sẽ chuyển hướng đến trang đăng nhập nếu người dùng không được phép. Dù sao thì cũng không sao vì bảo vệ thực sự là trên máy chủ.

Giải pháp 2: sử dụng trạng thái resolve

Trong giải pháp này, bạn sử dụng ui-routertính năng giải quyết .

Về cơ bản, bạn từ chối lời hứa resolvenếu người dùng không được xác thực và sau đó chuyển hướng họ đến trang đăng nhập.

Đây là cách nó diễn ra:

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()
      }
    }
  }
)

Không giống như giải pháp đầu tiên, giải pháp này thực sự ngăn chặn trạng thái đích tải.


6
@FredLackey cho biết người dùng chưa được xác thực đang ở state A. Họ nhấp vào một liên kết để đi đến protected state Bnhưng bạn muốn chuyển hướng họ đến logInPage. Nếu không $timeout, ui-routerđơn giản là sẽ dừng tất cả các chuyển đổi trạng thái, vì vậy người dùng sẽ bị mắc kẹt state A. Trước tiên, $timeoutcho phép ui-routerngăn chuyển đổi ban đầu sang protected state Bvì giải quyết bị từ chối và sau khi thực hiện xong, nó chuyển hướng đến logInPage.
MK Safi

Đâu là authenticatechức năng thực sự được gọi là?
CodyBugstein

Hàm @Imray authenticateđược truyền dưới dạng tham số ui-router. Bạn không cần phải gọi nó là chính mình. ui-routergọi nó
MK Safi

Tại sao bạn sử dụng '$ locationChangeSuccess' thay vì '$ stateChangeStart'?
Draex_ 8/8/2015

@ PeterDraexDräxler Tôi chủ yếu làm theo tài liệu. Bạn có nhận thấy sự khác biệt bằng cách sử dụng $stateChangeStart?
MK Safi

42

Giải pháp đơn giản nhất là sử dụng $stateChangeStartevent.preventDefault()hủy thay đổi trạng thái khi người dùng không được xác thực và chuyển hướng anh ta đến trạng thái xác thực đó là trang đăng nhập.

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

6
Tôi không nghĩ rằng điều này sẽ hoạt động nếu User.authenticaded () là một cuộc gọi không đồng bộ. Đó là chén thánh mọi người đang theo đuổi. Ví dụ: nếu mọi trạng thái ngoại trừ "đăng nhập" được bảo mật, tôi muốn xác nhận người dùng vẫn được xác thực TRƯỚC KHI tải bất kỳ trạng thái nào. Sử dụng các giải pháp hút vì chúng chỉ giải quyết một lần và để ngăn các trạng thái con tải, bạn phải đưa quyết định vào MỌI TRẺ .
Jason

xác thực không phải là một cuộc gọi không đồng bộ trong trường hợp của tôi: `this.authenticaded = function () {if (this.cienAccountID! == null) {return true; } trả về sai; }; `
sebest

Theo: stackoverflow.com/a/38374313/849829 , 'chạy' vượt lên trên 'dịch vụ' và do đó có vấn đề. Kiểm tra lưu trữ cục bộ cho trạng thái xác thực dường như là một cách tiếp cận tốt.
Deepak Thomas

22

Tôi nghĩ rằng bạn cần một servicexử lý quá trình xác thực (và lưu trữ của nó).

Trong dịch vụ này, bạn sẽ cần một số phương pháp cơ bản:

  • isAuthenticated()
  • login()
  • logout()
  • Vân vân ...

Dịch vụ này nên được đưa vào bộ điều khiển của bạn cho mỗi mô-đun:

  • Trong phần bảng điều khiển của bạn, hãy sử dụng dịch vụ này để kiểm tra xem người dùng có được xác thực không (service.isAuthenticated() phương thức) không. nếu không, hãy chuyển hướng đến / đăng nhập
  • Trong phần đăng nhập của bạn, chỉ cần sử dụng dữ liệu biểu mẫu để xác thực người dùng thông qua service.login()phương thức của bạn

Một ví dụ hay và mạnh mẽ cho hành vi này là ứng dụng góc cạnh của dự án và cụ thể là mô-đun bảo mật của nó dựa trên Mô-đun đánh chặn HTTP Auth tuyệt vời

Hi vọng điêu nay co ich


21

Tôi đã tạo mô-đun này để giúp tạo ra miếng bánh này

Bạn có thể làm những việc như:

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

Hoặc cũng

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

Đó là thương hiệu mới nhưng đáng để kiểm tra!

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


2
những gì ngăn tôi chỉnh sửa nguồn trong thời gian chạy và xóa 'quản trị viên' | | "Thần" và tiếp tục?
Pogrindis

12
Tôi hy vọng rằng bất kỳ yêu cầu dữ liệu nào cần sự cho phép cũng đang được xác minh tại máy chủ.
Ben Ripley

24
Điều này không có nghĩa là để bảo mật, ủy quyền phía khách hàng không bao giờ là vì bạn luôn có thể thay đổi các giá trị. Bạn thậm chí có thể chặn các phản hồi từ phía máy chủ và đánh giá chúng là "được ủy quyền" Điểm cho phép / ủy quyền ở phía máy khách là để tránh cho phép người dùng thực hiện các công cụ bị cấm cho mục đích ux. Ví dụ: nếu bạn đang xử lý một hành động chỉ dành cho quản trị viên, ngay cả khi người dùng lừa độc hại khách hàng để cho phép gửi yêu cầu bị hạn chế đến máy chủ, máy chủ vẫn sẽ trả lời phản hồi 401. Tất nhiên đây luôn là trách nhiệm của api đang được triển khai @BenRipley thực sự
Rafael Vidaurre

3
Câu trả lời tuyệt vời cho câu hỏi Rafael. Luôn luôn bảo vệ api bởi vì mặt trước là thứ dễ bị đảo ngược nhất, có thể giả mạo gần như có.
Frankie Loscavio

1
Vấn đề này với lịch sử đã được giải quyết khá lâu rồi @Bohdan. Bạn có thể sử dụng nó một cách an toàn ngay cả với các tính năng bổ sung của bộ định tuyến.
masterspambot

16

Tôi muốn chia sẻ một giải pháp khác làm việc với bộ định tuyến ui 1.0.0.X

Như bạn có thể biết, stateChangeStart và stateChangeSuccess hiện không được dùng nữa. https://github.com/angular-ui/ui-router/issues/2655

Thay vào đó, bạn nên sử dụng $ transitions http://angular-ui.github.io/ui-router/1.0.0-alpha.1/interfaces/transition.ihookregistry.html

Đây là cách tôi đạt được nó:

Đầu tiên tôi có và AuthService với một số chức năng hữu ích

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

Sau đó, tôi có cấu hình này:

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");
                    }
                });
            }]);

Bạn có thể thấy rằng tôi sử dụng

data: {
   authRequired: true
}

để đánh dấu trạng thái chỉ có thể truy cập nếu được xác thực.

sau đó, trên .sl tôi sử dụng các hiệu ứng chuyển tiếp để kiểm tra trạng thái được xác thực

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

Tôi xây dựng ví dụ này bằng cách sử dụng một số mã được tìm thấy trong tài liệu $ transitions. Tôi khá mới với bộ định tuyến ui nhưng nó hoạt động.

Hy vọng nó có thể giúp bất cứ ai.


Điều này là tuyệt vời cho những người sử dụng bộ định tuyến mới hơn. Cảm ơn!
mtro

5

Đây là cách chúng tôi đã thoát khỏi vòng lặp định tuyến vô hạn và vẫn được sử dụng $state.gothay vì$location.path

if('401' !== toState.name) {
  if (principal.isIdentityResolved()) authorization.authorize();
}

1
Có ai biết tại sao khi sử dụng câu trả lời / thiết lập được chấp nhận được mô tả ở trên thanh địa chỉ không còn hiển thị url và tất cả các đoạn và tham số chuỗi truy vấn không? Kể từ khi thực hiện điều này, thanh địa chỉ không còn cho phép ứng dụng của chúng tôi được đánh dấu.
Frankie Loscavio

1
Đây không phải là một nhận xét về một trong những câu trả lời hiện có sao? Bởi vì OP không có mã như vậy và thậm chí còn không rõ câu trả lời / mã này được đề cập đến
TJ

3

Tôi có một giải pháp khác: giải pháp đó hoạt động hoàn hảo khi bạn chỉ có nội dung bạn muốn hiển thị khi đăng nhập. Xác định quy tắc nơi bạn kiểm tra nếu bạn đã đăng nhập và không phải là đường dẫn của danh sách trắng.

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

Trong ví dụ của tôi, tôi hỏi nếu tôi chưa đăng nhập và tuyến hiện tại tôi muốn định tuyến không phải là một phần của '/ login', bởi vì các tuyến trong danh sách trắng của tôi là như sau

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

vì vậy tôi có quyền truy cập ngay vào hai tuyến này và mọi tuyến khác sẽ được kiểm tra nếu bạn trực tuyến.

Đây là toàn bộ tập tin định tuyến của tôi cho mô-đun đăng nhập

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 */ } là cú pháp ES6, sử dụng thay thế function() { /* code */ }


3

Sử dụng đánh chặn $ http

Bằng cách sử dụng công cụ chặn $ http, bạn có thể gửi các tiêu đề đến Back-end hoặc ngược lại và thực hiện kiểm tra theo cách đó.

Bài viết tuyệt vời về đánh chặn $ http

Thí dụ:

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

Đặt cái này trong hàm .config hoặc .run của bạn.


2

Trước tiên, bạn sẽ cần một dịch vụ mà bạn có thể đưa vào bộ điều khiển của mình có một số ý tưởng về trạng thái xác thực ứng dụng. Chi tiết xác thực bền vững với lưu trữ cục bộ là một cách tốt để tiếp cận nó.

Tiếp theo, bạn sẽ cần kiểm tra trạng thái xác thực ngay trước khi thay đổi trạng thái. Vì ứng dụng của bạn có một số trang cần được xác thực và những trang khác không có, hãy tạo tuyến phụ huynh kiểm tra xác thực và làm cho tất cả các trang khác yêu cầu giống nhau là con của cha mẹ đó.

Cuối cùng, bạn sẽ cần một số cách để biết liệu người dùng hiện đang đăng nhập của bạn có thể thực hiện một số thao tác nhất định hay không. Điều này có thể đạt được bằng cách thêm chức năng 'có thể' vào dịch vụ xác thực của bạn. Có thể có hai tham số: - hành động - bắt buộc - (tức là 'Manage_dashboards' hoặc 'create_new_dashboard') - đối tượng - tùy chọn - đối tượng đang được vận hành. Ví dụ: nếu bạn có một đối tượng bảng điều khiển, bạn có thể muốn kiểm tra xem nếu dashboard.ownerId === logInUser.id. (Tất nhiên, thông tin được truyền từ máy khách sẽ không bao giờ được tin cậy và bạn phải luôn xác minh thông tin này trên máy chủ trước khi ghi vào cơ sở dữ liệu của bạn).

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

** TUYÊN BỐ TỪ CHỐI: Mã trên là mã giả và không có bảo đảm **

Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.