javascript - programador - que es backend y frontend en el diseño web
Patrones de seguridad de JavaScript con autorización de back-end (7)
Estoy buscando algunos buenos recursos, patrones y prácticas sobre cómo manejar las necesidades básicas de seguridad, como la autorización, en el lado del cliente de JavaScript.
Estoy construyendo un sitio web con un sistema de fondo que ejecuta uno de los frameworks MVC comunes. El back-end manejará todas las necesidades reales de seguridad: autorización y autenticación. El frente se construirá con Backbone.js, jQuery y algunas otras bibliotecas para facilitar una experiencia de usuario muy rica.
Aquí hay un ejemplo de un escenario que necesito manejar:
Tengo una grilla de datos con algunos botones encima. Si selecciona un elemento en la cuadrícula, ciertos botones se habilitan para que pueda realizar esa acción en el elemento seleccionado. Esta funcionalidad es fácil de construir ...
Ahora necesito considerar la autorización. El servidor de fondo solo renderizará los botones con los que el usuario puede trabajar. Además, el servidor back-end verificará la autorización cuando el usuario intente realizar esa acción. ... por lo que el back-end está cubierto y el usuario no podrá hacer lo que intenta, si no está autorizado.
Pero, ¿qué pasa con el JavaScript? Si mi código está configurado con un grupo de manejadores de clics de jQuery u otros eventos que habilitan y deshabilitan botones, ¿cómo manejo los botones que no están allí? ¿Acabo de escribir un montón de feas if
declaraciones comprueban la existencia del botón? ¿O escribo el JavaScript de una manera que me permite enviar solo el JavaScript de los botones existentes, hasta el navegador, según la autorización? o ???
Ahora imagine una vista en árbol que puede o no permitir la funcionalidad de arrastrar y soltar, basada en la autorización ... y un formulario de agregar / editar que puede o no existir en función de la autorización ... y todas esas otras complicadas necesidades de autorización, con un gran cantidad de JavaScript para ejecutar esas piezas de la interfaz.
Estoy buscando recursos, patrones y prácticas para manejar este tipo de escenarios, donde el back-end maneja la autorización real, pero el front-end también necesita dar cuenta de las cosas que no están ahí basadas en la autorización.
Como mencionó la red troncal, su código no debería ser
un grupo de manejadores de clics de jQuery
pero en su lugar puede usar modelos para almacenar datos sobre la autorización y hacer que sus opiniones reaccionen en consecuencia.
Cualquier cosa en el lado del cliente puede ser pirateada. Entonces, lo que estamos haciendo en nuestra aplicación singlepage js son los derechos en el servidor. Combinamos una lista de derechos por usuario y tenemos un token de OAuth que le pasamos al cliente y se lo enviamos con cada solicitud. luego en el lado del servidor antes de que se tome una acción, vemos si el usuario está autenticado para esta acción.
Por supuesto, esto no lo protege de alguien en un café con fuego de chimenea ... pero ese es otro problema.
En el lado del cliente, deja que JQuery se encargue de eso. Si el elemento no está allí, no hace nada. Solo vincula los manejadores de eventos a los elementos que encuentra, no necesitará ninguna verificación if-an-element-exist
, solo use los selectores de JQuery.
Hay tres cosas bastante directas que pude ver:
Vistas modulares de Backbone Crecí mucho con las vistas de Backbone anidadas y altamente modulares. Es decir, cada fila en su árbol podría ser una vista de Backbone y reaccionar a sus propios requisitos de autorización.
Hashes de eventos múltiples Configure hashes de eventos múltiples en la vista que cambie con delegateEvents () en función de sus requisitos de autorización y desencadenados por un evento. De esa forma puedes evitar un conjunto de declaraciones feas.
Múltiples plantillas De forma similar, especifique varias plantillas para procesar según los requisitos de autorización.
Los tres requerirían una configuración de evento configurada (por ejemplo, utilizando su propio controlador ventSub PubSub) donde active las comprobaciones de autorización en función de la respuesta de una solicitud de servidor RESTful o basada en alguna función del lado del cliente.
La forma en que manejamos esto es dejar que el servidor nos envíe el html que necesita ser procesado en base a la decisión o decisiones que resultan en una consecuencia. algo como esto
this.ActionFor(Model)
.Decision<Authentication1>()
.Decision<Authentication2>()
.Consequence<HTMLRenderer>()
Probablemente podría usar este patrón de manera similar en JS si cree que era necesario.
La forma en que manejo la autenticación en el cliente es tener un modelo Backbone singleton que tenga un valor isAuthenticated que inicialmente llene desde el servidor con:
@{
App.user.set({ isAuthenticated: @UserSession.IsAuthenticated.ToString().ToLower() });
}
Luego, todos los controles / funcionalidades de javascript que cambian el comportamiento basado en la autenticación básicamente solo escuchan los cambios en este campo y se vuelven a presentar en el estado correcto. Toda esta vista / lógica está hecha con JavaScript, por lo que funciona en el momento de la generación de la página (por el servidor) o en el cliente con Javascript / ajax.
No mantengo los controladores de eventos en la funcionalidad existente / oculta, creo de nuevo todos los elementos de la interfaz de usuario y vuelvo a conectar todos los controladores de eventos una vez que las vistas se vuelven a generar en función del indicador isAuthenticated.
Lo bueno es que una vez que inicie sesión con ajax en el cliente (es decir, después de que se haya procesado la página del servidor) solo tiene que establecer el mismo campo y como por arte de magia (Backbone FTW :) todo funciona y se procesa correctamente.
Yo recomendaría usar el patrón Factory junto con las propiedades de clase de Backbone para inicializar diferentes vistas dependiendo de la autorización del usuario. En el siguiente ejemplo, defino un GridView base que contiene todos los comportamientos predeterminados y comunes. AdminGridView y EditorGridView contiene la funcionalidad específica del usuario autorizado. En el ejemplo simplificado a continuación, un manejador de clic solo estará conectado a los administradores.
Lo bueno es que todo está encapsulado, de modo que solo la fábrica necesita saber sobre AdminGridView y EditorGridView. Tu código solo interactuaría con GridView.
// GridView is an abstract class and should not be invoked directly
var GridView = Backbone.View.extend({
// put all common / default code here
_template: _.template($(''#grid'').html()),
initialize: function(){
this.model.bind(''change'', this.render, this);
}.
onButtonClick: function(){
// do something
},
render: function(){
$(this.el).html(this._template(this.model));
}
}, {
create: function (options) {
switch (options.authorization.get(''type'')) {
case ''admin'':
return new AdminGridView(options);
case ''editor'':
return new EditorGridView(options);
default:
throw new Error(''Authorization type '' + options.authorization.get(''type'') + '' not supported.'');
}
}
});
var AdminGridView = GridView.extend({
// put admin specific code here
events: {
''click .button'': ''onButtonClick''
}
});
var EditorGridView = GridView.extend({
// put editor specific code here
});
var authorization = new Backbone.Model({ type: ''admin'' });
var gridView = GridView.create({ model: someModel, authorization: authorization });
$(''body'').append((gridView.render().el))