Maison > Article > interface Web > Implémentation de fonctions d'authentification d'identité et de vérification de formulaire pour l'accès des utilisateurs dans AngularJS_AngularJS
Authentification
La conception d'autorisations la plus courante est le contrôle d'accès basé sur les rôles RBAC. L'idée de base est que diverses autorisations pour les opérations du système ne sont pas directement accordées à des utilisateurs spécifiques, mais qu'un ensemble de rôles est établi entre l'ensemble d'utilisateurs et l'ensemble d'autorisations. Chaque rôle correspond à un ensemble d'autorisations correspondant.
Une fois qu'un utilisateur se voit attribuer le rôle approprié, il dispose de toutes les autorisations d'opération de ce rôle. L'avantage est que vous n'avez pas besoin d'attribuer des autorisations à chaque fois que vous créez un utilisateur. Il vous suffit d'attribuer le rôle correspondant à l'utilisateur. De plus, les modifications d'autorisation des rôles sont bien inférieures aux modifications d'autorisation des utilisateurs. ce qui simplifiera la gestion des autorisations des utilisateurs et réduira la surcharge du système.
Dans l'application d'une seule page construite par Angular, nous devons faire quelques choses supplémentaires pour implémenter une telle architecture. En termes de projet global, il y a environ trois endroits que les ingénieurs front-end doivent gérer
.
1. Traitement de l'interface utilisateur (déterminer si certains contenus de la page sont affichés en fonction des autorisations dont dispose l'utilisateur)
2. Traitement du routage (lorsque l'utilisateur accède à une URL à laquelle il n'est pas autorisé à accéder, passez à une page d'erreur)
3. Traitement des requêtes HTTP (lorsque nous envoyons une requête de données, si le statut renvoyé est 401 ou 403, elle est généralement redirigée vers une page d'erreur)
Mise en place du contrôle d'identité d'accès
Tout d'abord, nous devons obtenir toutes les autorisations de l'utilisateur actuel avant le démarrage d'Angular, puis une manière plus élégante consiste à stocker cette relation de mappage via un service pour le traitement de l'interface utilisateur si le contenu d'une page est affiché en fonction des autorisations. , nous devrions utiliser Une directive implémentée. Après les avoir traités, nous devons également y ajouter un attribut "permission" supplémentaire lors de l'ajout d'une route, et attribuer une valeur pour indiquer quels rôles ont l'autorisation d'accéder à cette URL, puis écouter. à l'événement routeChangeStart via Angular Pour vérifier si l'utilisateur actuel a les droits d'accès à cette URL Enfin, un intercepteur HTTP est nécessaire pour surveiller lorsque l'état renvoyé par une requête est 401 ou 403, et accéder à une page d'erreur. le travail. Cela semble beaucoup, mais c'est en fait assez facile à gérer un par un.
Renvoyez 401, exécutez loginCtrl, retournez 403, exécutez PermissionCtrl.
Obtenez la relation de mappage d'autorisation avant l'exécution d'Angular
Le projet Angular est démarré via ng-app, mais dans certains cas, nous souhaitons que le démarrage du projet Angular soit sous notre contrôle. Par exemple, dans ce cas, j'espère obtenir toutes les relations de mappage d'autorisations de l'utilisateur actuellement connecté. , puis démarrez l'application Angular. Heureusement, Angular lui-même fournit cette méthode, qui est angulaire.bootstrap().
var permissionList; angular.element(document).ready(function() { $.get('/api/UserPermission', function(data) { permissionList = data; angular.bootstrap(document, ['App']); }); });
Ceux qui regardent attentivement remarqueront peut-être que $.get() est utilisé ici. Il est correct d'utiliser jQuery au lieu de $resource ou $http d'Angular, car Angular n'a pas été démarré pour le moment. encore.
En utilisant davantage le code ci-dessus, vous pouvez mettre la relation de mappage obtenue dans un service et l'utiliser comme variable globale.
// app.js var app = angular.module('myApp', []), permissionList; app.run(function(permissions) { permissions.setPermissions(permissionList) }); angular.element(document).ready(function() { $.get('/api/UserPermission', function(data) { permissionList = data; angular.bootstrap(document, ['App']); }); }); // common_service.js angular.module('myApp') .factory('permissions', function ($rootScope) { var permissionList; return { setPermissions: function(permissions) { permissionList = permissions; $rootScope.$broadcast('permissionsChanged') } }; });
Après avoir obtenu l'ensemble d'autorisations de l'utilisateur actuel, nous avons archivé cet ensemble dans un service correspondant, puis avons effectué deux autres choses :
(1) Stockez les autorisations dans la variable d'usine afin qu'elle reste en mémoire pour remplir le rôle de variables globales sans polluer l'espace de noms.
(2) Diffusez les événements via $broadcast lorsque les autorisations changent.
1. Comment déterminer si les composants de l'interface utilisateur sont visibles ou masqués en fonction des autorisations
Ici, nous devons écrire nous-mêmes une directive, qui affichera ou masquera les éléments en fonction des relations d'autorisation
<!-- If the user has edit permission the show a link --> <div has-permission='Edit'> <a href="/#/courses/{{ id }}/edit"> {{ name }}</a> </div> <!-- If the user doesn't have edit permission then show text only (Note the "!" before "Edit") --> <div has-permission='!Edit'> {{ name }} </div>
Ici, nous voyons que la situation idéale est de passer un attribut has-permission pour vérifier le nom de l'autorisation. Si l'utilisateur actuel en a une, elle sera affichée, sinon, elle sera masquée.
angular.module('myApp').directive('hasPermission', function(permissions) { return { link: function(scope, element, attrs) { if(!_.isString(attrs.hasPermission)) throw "hasPermission value must be a string"; var value = attrs.hasPermission.trim(); var notPermissionFlag = value[0] === '!'; if(notPermissionFlag) { value = value.slice(1).trim(); } function toggleVisibilityBasedOnPermission() { var hasPermission = permissions.hasPermission(value); if(hasPermission && !notPermissionFlag || !hasPermission && notPermissionFlag) element.show(); else element.hide(); } toggleVisibilityBasedOnPermission(); scope.$on('permissionsChanged', toggleVisibilityBasedOnPermission); } }; });
angular.module('myApp') .factory('permissions', function ($rootScope) { var permissionList; return { setPermissions: function(permissions) { permissionList = permissions; $rootScope.$broadcast('permissionsChanged') }, hasPermission: function (permission) { permission = permission.trim(); return _.some(permissionList, function(item) { if(_.isString(item.Name)) return item.Name.trim() === permission }); } }; });
2. Accès basé sur les autorisations sur l'itinéraire
L'idée d'implémenter cette partie est la suivante : lorsque nous définissons une route, ajoutez un attribut d'autorisation. La valeur de l'attribut correspond aux autorisations d'accès à l'url actuelle. Ensuite, l'événement routeChangeStart est utilisé pour surveiller. L'URL change. Chaque fois que l'URL change Quand , vérifiez si l'URL actuelle à sauter répond aux conditions, puis décidez si vous souhaitez sauter avec succès ou accéder à la mauvaise page d'invite .
app.config(function ($routeProvider) { $routeProvider .when('/', { templateUrl: 'views/viewCourses.html', controller: 'viewCoursesCtrl' }) .when('/unauthorized', { templateUrl: 'views/error.html', controller: 'ErrorCtrl' }) .when('/courses/:id/edit', { templateUrl: 'views/editCourses.html', controller: 'editCourses', permission: 'Edit' }); });
app.controller('mainAppCtrl', function($scope, $location, permissions) { $scope.$on('$routeChangeStart', function(scope, next, current) { var permission = next.$$route.permission; if(_.isString(permission) && !permissions.hasPermission(permission)) $location.path('/unauthorized'); }); });
这里依然用到了之前写的hasPermission,这些东西都是高度可复用的.这样就搞定了,在每次view的route跳转前,在父容器的Controller中判断一些它到底有没有跳转的权限即可.
3.HTTP请求处理
这个应该相对来说好处理一点,思想的思路也很简单.因为Angular应用推荐的是RESTful风格的借口,所以对于HTTP协议的使用很清晰.对于请求返回的status code如果是401或者403则表示没有权限,就跳转到对应的错误提示页面即可.
当然我们不可能每个请求都去手动校验转发一次,所以肯定需要一个总的filter.代码如下:
angular.module('myApp') .config(function($httpProvider) { $httpProvider.responseInterceptors.push('securityInterceptor'); }) .provider('securityInterceptor', function() { this.$get = function($location, $q) { return function(promise) { return promise.then(null, function(response) { if(response.status === 403 || response.status === 401) { $location.path('/unauthorized'); } return $q.reject(response); }); }; }; });
写到这里就差不多可以实现在这种前后端分离模式下,前端部分的权限管理和控制了。
表单验证
AngularJS 前端验证指令
var rcSubmitDirective = { 'rcSubmit': function ($parse) { return { restrict: "A", require: [ "rcSubmit", "?form" ], controller: function() { this.attempted = false; var formController = null; this.setAttempted = function() { this.attempted = true; }; this.setFormController = function(controller) { formController = controller; }; this.needsAttention = function(fieldModelController) { if (!formController) return false; if (fieldModelController) { return fieldModelController.$invalid && (fieldModelController.$dirty || this.attempted); } else { return formController && formController.$invalid && (formController.$dirty || this.attempted); } }; }, compile: function() { return { pre: function(scope, formElement, attributes, controllers) { var submitController = controllers[0]; var formController = controllers.length > 1 ? controllers[1] : null; submitController.setFormController(formController); scope.rc = scope.rc || {}; scope.rc[attributes.name] = submitController; }, post: function(scope, formElement, attributes, controllers) { var submitController = controllers[0]; var formController = controllers.length > 1 ? controllers[1] : null; var fn = $parse(attributes.rcSubmit); formElement.bind("submit", function(event) { submitController.setAttempted(); if (!scope.$$phase) scope.$apply(); if (!formController.$valid) return; scope.$apply(function() { fn(scope, { $event: event }); }); }); } }; } }; } };
验证通过
<form name="loginForm" novalidate ng-app="LoginApp" ng-controller="LoginController" rc-submit="login()"> <div class="form-group" ng-class="{'has-error': rc.loginForm.needsAttention(loginForm.username)}"> <input class="form-control" name="username" type="text" placeholder="Username" required ng-model="session.username" /> <span class="help-block" ng-show="rc.form.needsAttention(loginForm.username) && loginForm.username.$error.required">Required</span> </div> <div class="form-group" ng-class="{'has-error': rc.loginForm.needsAttention(loginForm.password)}"> <input class="form-control" name="password" type="password" placeholder="Password" required ng-model="session.password" /> <span class="help-block" ng-show="rc.form.needsAttention(loginForm.password) && loginForm.password.$error.required">Required</span> </div> <div class="form-group"> <button type="submit" class="btn btn-primary pull-right" value="Login" title="Login"> <span>Login</span> </button> </div> </form>
样式如下
前端验证通过会调用login()。