#angularjs #spring-mvc #spring-security #authorization
#angularjs #spring-mvc #spring-безопасность #авторизация
Вопрос:
У меня есть приложение, которое использует Spring Security для аутентификации / авторизации на стороне сервера, Spring MVC для конечных точек на стороне сервера REST и AngularJS для просмотра.
На стороне сервера я реализовал все фильтры, необходимые для доступа ко всем этим конечным точкам REST, на основе прав пользователя. Мой вопрос в том, как мне следует подходить к созданию видимых / скрытых элементов html на основе аутентифицированных прав ПОЛЬЗОВАТЕЛЯ?
Например, у меня есть в представлении 3 кнопки (button1, button2, button3). Каждая кнопка имеет соответствующее ПРАВО ПОЛЬЗОВАТЕЛЯ, что должно сделать их видимыми / скрытыми. Давайте назовем эти права USER_RIGHT1, USER_RIGHT2, USER_RIGHT3.
Если у пользователя есть правильный USER_RIGHT1, он должен видеть в представлении button1, если у него есть правильный USER_RIGHT2, он должен видеть в представлении button2 и так далее.
Мой подход заключался в том, чтобы иметь список аутентифицированных прав пользователя в клиенте и сделать что-то в следующем примере:
<div ng-if="rights contains USER_RIGHT1">
<button name="button1".... />
</div>
<div ng-if="rights contains USER_RIGHT2">
<button name="button2".... />
</div>
Я не уверен, должен ли список прав аутентифицированного пользователя быть в клиенте.
Как мне подойти к этой проблеме? Правильно ли я это делаю?
Ответ №1:
Мой подход в основном такой, какой вы предлагаете.
У вас может быть фабрика, которая хранит массив разрешений пользователя и имеет функцию для проверки наличия разрешения в массиве:
.factory('Auth', function() {
var permissions = [];
return {
allowed : function(permission) {
return _.contains(permissions, permission);
}
};});
Затем у вас может быть директива, которая показывает / скрывает элемент с помощью сервиса:
.directive('allowed', function(Auth){
return {
link : function(scope, elem, attr) {
if(!Auth.allowed(attr.allowed)){
elem.hide();
}
}
};
});
Итак, в ваших представлениях вам нужно только сделать:
<div allowed="permission_name"> </div>
Комментарии:
1. Спасибо за ваш комментарий. Я нахожу этот подход очень простым, чистым и полезным.
2. Я не понимаю, почему это не является принятым решением.
Ответ №2:
Безопасность на клиенте, т. е. в браузере, практически бесполезна. Однако он должен присутствовать, чтобы обычный пользователь не мог видеть то, чего он не должен, однако сервер должен быть конечным местом, где обеспечивается безопасность.
Я бы создал быструю директиву для отображения / скрытия или компонентов пользовательского интерфейса и создал службу аутентификации для выполнения фактической логики, чтобы определить, имеет ли пользователь правильные права.
Я нахожусь на 60% пути, написав подробную статью об авторизации в AngularJS в своем блоге. Я бы проверил примерно через неделю, и я должен это сделать — это также может помочь вам с авторизацией маршрута.
ОБНОВЛЕНИЕ: сообщение в блоге об авторизации и безопасности angular route можно найти здесь
По сути, служба авторизации авторизует пользователя с помощью вашей серверной службы, а затем сохраняет их права на приложения.
Затем директива будет использовать эту службу, чтобы определить, имеет ли пользователь достаточно прав для просмотра компонента пользовательского интерфейса.
Я не тестировал приведенный ниже код, поэтому вам может потребоваться его отладка.
angular.module('myApp').factory('authService', [
function () {
var loggedInUser,
login = function (email, password) {
//call server and rights are returned
//loggedInUser is assigned
},
hasSecurityRoles = function (requiredRoles) {
var hasRole = false,
roleCheckPassed = false,
loweredRoles;
if (loggedInUser === undefined) {
hasRole = false;
} else if (loggedInUser !== undefined amp;amp; requiredRoles === undefined) {
hasRole = true;
} else if (loggedInUser !== undefined amp;amp; requiredRoles !== undefined) {
if (requiredRoles.length === 0) {
hasRole = true;
} else if (loggedInUser.permittedActions === undefined) {
hasRole = false;
} else {
loweredRoles = [];
angular.forEach(loggedInUser.permittedActions, function (role) {
loweredRoles.push(role.name.toLowerCase());
});
// check user has at least one role in given required roles
angular.forEach(requiredRoles, function (role) {
roleCheckPassed = roleCheckPassed || _.contains(loweredRoles, role.toLowerCase());
});
hasRole = roleCheckPassed;
}
}
return hasRole;
};
return {
login: login,
hasSecurityRoles: hasSecurityRoles
};
}
]);
angular.module('myApp').directive('visibleToRoles', [
'authService',
function (authService) {
return {
link: function (scope, element, attrs) {
var makeVisible = function () {
element.removeClass('hidden');
},
makeHidden = function () {
element.addClass('hidden');
},
determineVisibility = function (resetFirst) {
if (resetFirst) {
makeVisible();
}
if (authService.hasSecurityRoles(roles)) {
makeVisible();
} else {
makeHidden();
}
},
roles = attrs.visibleToRoles.split(',');
if (roles.length > 0) {
determineVisibility(true);
}
}
};
}
]);
Затем вы могли бы использовать его следующим образом
<div visible-to-role="admin,usermanager">.....</div>
Комментарии:
1. Мне очень нравится этот подход, а также подход @cuttlas. Но использование этих прав, жестко запрограммированных в директиве visible-to-role, делает их по-прежнему видимыми для клиента (в инструментах разработки, таких как chrome). Можно ли что-то сделать в AngularJS, чтобы стереть эту директиву и ее параметры после ее оценки? Например, если я проверяю элементы в chrome в этом div, я действительно не хочу видеть жестко запрограммированный «admin, usermanager» (в моем примере я не хочу, чтобы клиенты проверяли права, необходимые им для выполнения определенных действий, или чтобы увидеть, что другие пользователи могут делать на этой странице)
2. А также мне действительно не нравится, что где-то в моем javascript есть список прав аутентифицированного пользователя. Даже если сервер не разрешает доступ к конечным точкам REST, пользователь может манипулировать этим объектом и предоставлять себе другие права. Я далеко думаю? Или я должен придерживаться простых вещей? 🙂
3. Однако, даже если вы удалите атрибут после того, как директива будет связана ‘el.removeAttr (‘visible-to-role’)’, я мог бы просто посмотреть ваш код javascript или, что еще лучше, изменить директиву. Так что вам действительно не следует думать, что что-либо за пределами вашего сервера в любом случае безопасно. Проще обмануть 99% вашей пользовательской базы — безопасность всегда должна выполняться на сервере.
4. Как описано ниже, многие из них вы можете использовать доступ на основе функций, а не доступ на основе ролей. Поэтому было бы сложно составить представление о том, кто что может делать.
Ответ №3:
Вместо того, чтобы иметь жестко запрограммированный список в ваших шаблонах / страницах, вы можете получить список прав аутентифицированного пользователя с сервера и загрузить его в свою область, а затем сделать то же самое, что вы делаете. Если вы используете ui-router, это возможно с помощью свойства resolve (то есть предварительной загрузки определенных данных, возможно, с сервера перед вызовом контроллера).
Таким образом, вы можете получить права только для пользователя, который просматривает страницу, а не жестко прописывать все права в клиенте.
Комментарии:
1. Спасибо за комментарий. Я думаю, что я не очень хорошо объяснил свой подход, но я делаю именно то, что вы говорите. Единственными жестко запрограммированными правами являются права, используемые в условиях ng-if, и я бы хотел, чтобы пользователь не мог видеть эти жестко запрограммированные права (например, если я открою dev-tools из chrome, я могу увидеть все эти жестко запрограммированные права…
2. Затем вы можете вместо отправки прав отправить что-то вроде «show_feature1», «show_feature2», где значения «show_xxx» устанавливаются на стороне сервера. Следовательно, то, что видит пользователь, намного более абстрактно, чем имя конкретного права. Кроме этого, я думаю, что ваш подход хорош.