Ответ 1
Мне пришлось решить подобную проблему в приложении, которое я сейчас разработал, но разрешения основаны на стране пользователя.
Я знаю, что вы уже есть, но просто для повторения в пользу любого читателя, разрешения должны всегда выполняться на стороне сервера, а безопасность JavaScript всегда является вторичной. Это связано с тем, что любой, кто имеет половину мозга, может использовать либо букмарклет, либо Firebug для выполнения произвольного JavaScript и обойти защиту на стороне клиента.
Я обнаружил, что есть несколько способов сделать это, но есть, в частности, два подхода, которые являются самыми разумными. Независимо от подхода, есть две вещи, которые следует учитывать: 1) какой JavaScript для сервера и как избежать ненужной логики, и 2) как избежать выполнения логики, недоступной для пользователя.
Конфигурация ленивых загрузок и разрешений:
-
Все виджеты в моем приложении ленивы загружаются через
dojo.require
, поэтому не нужно беспокоиться о ненужном включении JavaScript, который не был применим к пользователю. Я не очень хорошо знаком с библиотекой ExtJs, но, насколько я видел, он не дает сопоставимого метода; однако важно, чтобы синхронный вызов ajax сопровождался eval. В любом случае в этом методе важно, чтобы функциональность компонента не пересекалась через разные файлы (также, как правило, это хороший дизайн). Каждый файл должен быть его собственным классом, который управляет определенным виджетами или другим элементом пользовательского интерфейса. -
Затем я устанавливаю разрешения в созданном сервером классе JavaScript. Затем этот класс может ссылаться во всем приложении на то, что есть и что не разрешено.
Например, следующий метод контролировал доступ к большинству виджетов, доступных из глобальных элементов управления.
com.project.frontController.prototype.init = function(widgets) {
var permissions = com.project.config.permissions;
// For each widget in the controller
for ( var i=0, l=widgets.length; i<l; ++i ) {
// If access is restricted to the widget (by id), then disable it.
if ( !permissions[ widgets[i].id ] {
com.project.util.disable(widgets[i].btnAccessNode);
}
// Otherwise, leave it enabled and connect the necessary event handlers.
else {
// connect an onclick handler or whatever is required for the widget
}
}
};
И config выглядел примерно так:
com.project.config.permissions = {
"widgetAbc": {
btnAccessNode: "#some-css-selector",
otherWidgetConfig: "etc"
},
"widgetXyz": {
btnAccessNode: "div.some-css-selector"
}
};
Проверка компиляции и функциональности:
-
Некоторые приложения скомпилируют весь свой JavaScript в один файл, а затем передадут его клиенту. Если ваше приложение делает такую вещь, и вы можете это сделать динамически, все необходимые JS можно определить на стороне сервера, прежде чем он будет обслуживаться. Конечно, это наберет некоторые издержки; однако вы можете кэшировать скомпилированные версии по ролям. Если роли немного, то этот кеш может быть легко загрунтован, и это просто вопрос включения одного конкретного script.
-
Поскольку доступен только разрешенный JavaScript, просто обнаружив, что доступный класс/функция доступен, прежде чем пытаться выполнить его, все, что вам нужно сделать для проверки прав доступа.
Например, включение script может выглядеть так:
<script type="text/javascript" src="/js/compiler.php?role=moderator"></script>
И ваша проверка работоспособности будет выглядеть примерно так:
com.project.frontController.prototype.init = function(widgets) {
// For each widget in the controller
for ( var i=0, l=widgets.length; i<l; ++i ) {
// If the widget controller doesn't exist, disable it.
if ( !com.project.util.widgetExists[ widgets[i].id ] {
com.project.util.disable(widgets[i].btnAccessNode);
}
// Otherwise, leave it enabled and connect the necessary event handlers.
else {
// connect an onclick handler or whatever is required for the widget
}
}
};
или
com.project.someWidget.prototype.launchOtherWidget = function() {
if ( typeof otherWidget != "undefined" ) {
(new otherWidget()).open();
}
};
Обратите внимание, что оба метода очень похожи в реализации. Я бы сказал, что лучший способ сделать выбор между ними - это рассмотреть размер вашей кодовой базы, инструменты, доступные вам для компиляции "на лету", и кэшировать эти скомпилированные пакеты на основе ролей. Для моего проекта не только компилятор не был доступен в среде, но база кода была большой (1.3mb завышена /296kb по умолчанию плюс библиотека dojo), что было неприемлемо, так как клиент больше интересовался поддержанием низких время загрузки приложения.