[英]Handling authentication with angular front-end -> How to protect specific pages?
Looking to build web app in Node.js with ability for user to log in (authentication), which has 3 non secure pages (/home, /contact, /about) and one secure page (/admin). 希望在Node.js中构建具有用户登录(身份验证)功能的Web应用程序,该应用程序具有3个非安全页面(/ home,/ contact,/ about)和一个安全页面(/ admin)。 As an aside, I've been referencing the scotch.io Mean Machine book. 顺便说一句,我一直在参考scotch.io Mean Machine书。
The issue I'm having is that I've build everything out, and the login mechanism works in that when I log in, I get directed to /admin; 我遇到的问题是我已经将所有内容都构建好了,登录机制的工作原理是:登录时,我被定向到/ admin; however, when I go to /admin in the URL without logging in, I can still access the page. 但是,当我不登录而进入URL中的/ admin时,仍可以访问该页面。 Ie I'm not sure where to put the actual protection. 也就是说,我不确定将实际保护放在何处。
A bit below on how I've laid out my app. 下面是我如何布置应用程序的信息。 Hoping for as much a conceptual answer to suggest how I should be doing things, rather than necessarily only a code answer. 希望有一个概念上的答案来建议我应该如何做,而不是仅是代码答案。
Services: 服务:
Router: 路由器:
angular.module('routerRoutes', ['ngRoute'])
.config(function($routeProvider, $locationProvider) {
$routeProvider
.when('/', {
templateUrl: 'views/home.html',
controller: 'homeController',
controllerAs: 'home'
})
.when('/about', {
templateUrl: 'views/about.html',
controller: 'aboutController',
controllerAs: 'about'
})
.when('/contact', {
templateUrl: 'views/contact.html',
controller: 'contactController',
controllerAs: 'contact'
})
.when('/login', {
templateUrl: 'views/login.html',
controller: 'adminController',
controllerAs: 'login'
})
.when('/admin', {
templateUrl: 'views/admin/admin.html',
controller: 'adminController',
controllerAs: 'admin'
});
$locationProvider.html5Mode(true);
});
Controllers: 控制器:
homeController, aboutController, contactController are generally empty for now homeController,aboutController和contactController通常现在为空
adminController: adminController:
.controller('adminController', function($rootScope, $location, Auth) { .controller('adminController',function($ rootScope,$ location,Auth){
var vm = this; vm.loggedIn = Auth.isLoggedIn(); $rootScope.$on('$routeChangeStart', function() { vm.loggedIn = Auth.isLoggedIn(); window.alert(vm.loggedIn); // this gives correct answer and works Auth.getUser() .success(function(data) { vm.user = data; }); }); vm.doLogin = function() { vm.error = ''; Auth.login(vm.loginData.username, vm.loginData.password) .success(function(data) { vm.user = data.username; if (data.success) $location.path('/admin'); else vm.error = data.message; }); }; vm.doLogout = function() { Auth.logout(); vm.user = {}; $location.path('/login'); };
}); });
And finally, below is my index.html (just the body): 最后,下面是我的index.html(仅是正文):
<body class="container" ng-app="meanApp" ng-controller="adminController as admin">
<a href="/"><i class="fa fa-home">Home </i></a>
<a href="/about"><i class="fa fa-shield">About </i></a>
<a href="/contact"><i class="fa fa-comment">Contact</i></a>
<a href="/admin"><i class="fa fa-comment">Admin</i></a>
<ul class="nav navbar-nav navbar-right">
<li ng-if="!admin.loggedIn"><a href="/login">Login</a></li>
<li ng-if="admin.loggedIn" class="navbar-text">Hello {{ admin.user.username }}</li>
<li ng-if="admin.loggedIn"><a href="#" ng-click="admin.doLogout()">Logout</a></li>
</ul>
<main>
<div ng-view>
</div>
</main>
</body>
I won't paste the other html pages that get injected into since there isn't anything on them yet (the login.html has just the two input fields and submit button). 我将不会粘贴注入的其他html页面,因为它们上还没有任何东西(login.html只有两个输入字段和提交按钮)。
So a couple of questions: 有几个问题:
Another nit: 另一个优点:
I took a custom property route to secure the routes in my application. 我采用了自定义属性路由来保护应用程序中的路由。 Every state change taking place is listened for and inspected if it has this property. 侦听并检查每个发生的状态更改是否具有此属性。 If it has this property set then it checks if user is logged in, if they are not, it routes them to the 'login' state. 如果设置了此属性,则它将检查用户是否已登录;如果未登录,则会将其路由到“登录”状态。
I used UI-ROUTER in my current project where I have implemented this. 我在实现该功能的当前项目中使用了UI-ROUTER。 I made a custom parameter called "data" that I used within the route. 我在路由中使用了一个自定义参数“ data”。
Within a .config block to declare my opening routes: 在.config块中声明我的开放路线:
$stateProvider
.state('login', {
url: '/login',
templateUrl: 'login/login.html',
controller: 'LoginController',
controllerAs: 'vm'
})
.state('home', {
url: '',
templateUrl: 'layout/shell.html',
controller: 'ShellController',
controllerAs: 'vm',
data: {
requireLogin: true
}
})
Then I add this to a .run on the application where I'm looking for ui-router's $stateChangeStart event and looking at my custom property ('data') on the state declaration: 然后,将其添加到应用程序中的.run,在其中查找ui路由器的$ stateChangeStart事件并在状态声明中查看我的自定义属性(“数据”):
$rootScope.$on('$stateChangeStart', function (event, toState, toParams, fromState, fromParams) {
var requireLogin = toState.hasOwnProperty('data') && toState.data.requireLogin;
if (requireLogin && !authService.isLoggedIn()) {
event.preventDefault();
authService.setDestinationState(toState.name);
$state.go('login');
}
if (toState.name !== 'login') {
authService.setDestinationState(toState.name);
}
});
In case you're wondering what the authService.setDestinationState does... it preserves the URL that the user was attempting to visit... once they successfully login it forwards them to that state automagically (see below): 如果您想知道authService.setDestinationState的功能是什么...它将保留用户尝试访问的URL ...一旦成功登录,它将自动将其转发到该状态(请参见下文):
function login() {
authService.authLogin(vm.credentials)
.then(loginComplete)
.catch(loginFailed);
function loginComplete(data, status, headers, config) {
vm.user = data;
$rootScope.$broadcast('authorized');
$state.go(authService.getDestinationState());
}
function loginFailed(status) {
console.log('XHR Failed for login.');
vm.user = undefined;
vm.error = 'Error: Invalid user or password. ' + status.error;
toastr.error(vm.error, {closeButton: true} );
}
}
When you define your Admin route, you can define a property called resolve. 定义管理路由时,可以定义一个名为resolve的属性。 Each property within resolve is a function (it can be an injectable function). resolve中的每个属性都是一个函数(可以是可注入函数)。 This function should return a promise, the promise's result can be injected into the controller. 这个函数应该返回一个promise,promise的结果可以注入到控制器中。
For more information on resolve, look at http://odetocode.com/blogs/scott/archive/2014/05/20/using-resolve-in-angularjs-routes.aspx . 有关解决的更多信息,请参见http://odetocode.com/blogs/scott/archive/2014/05/20/using-resolve-in-angularjs-routes.aspx 。
You can use resolve as follows to do an authentication check. 您可以按以下方式使用resolve进行身份验证检查。
var authenticateRoute = ['$q', '$http' , function ($q, $http) {
var deferred = $q.defer();
$http.get("http://api.domain.com/whoami")
.then(function(response) {
if (response.data.userId) deferred.resolve(response.data.userId);
else window.location.href = "#/Login"
});
return deferred.promise();
}]
// ...
.when('/admin', {
templateUrl: 'views/admin/admin.html',
controller: 'adminController',
controllerAs: 'admin',
resolve: {
authenticatedAs: authenticateRoute
}
});
With this you could pass the authenticated User Id through - even if null - and let the controller deal with it, if for instance, you want a contextual message. 这样,您可以传递经过身份验证的用户ID(即使为null),也可以让控制器处理它,例如,如果您需要上下文消息。 Else, you could do as above and only do so if there is a user Id from the authentication request, otherwise redirect to your login route. 否则,您可以执行上述操作,并且仅在身份验证请求中有用户ID时才这样做,否则重定向到您的登录路由。
Hope this helps! 希望这可以帮助! /AJ / AJ
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.