2010-06-09 6 views
8

Estamos utilizando ASP.NET con muchas llamadas a AJAX "Método de página". Los servicios web definidos en la página invocan métodos de nuestro BusinessLayer. Para evitar que los hackers llamen a los métodos de página, queremos implementar cierta seguridad en BusinessLayer.Asegure un método de la capa empresarial. mejor práctica/mejor patrón

Estamos luchando con dos problemas diferentes.

primera:

public List<Employees> GetAllEmployees() 
{ 
    // do stuff 
} 

este método debe ser llamado por los Usuarios Autorizados con el papel "HR".

segunda:

public Order GetMyOrder(int orderId) 
{ 
    // do sutff 
} 

Este método sólo debe ser llamado por el propietario de la Orden.

sé que es fácil de implementar la seguridad para cada método como:

public List<Employees> GetAllEmployees() 
{ 
    // check if the user is in Role HR 
} 

o

public Order GetMyOrder(int orderId) 
{ 
    // check if the order.Owner = user 
} 

Lo que estoy buscando es algún patrón/las mejores prácticas para implementar este tipo de seguridad de una manera genérica (sin codificar el if if else else cada vez) Espero que entiendas lo que quiero decir :-)

+1

Veo que ha estado en SO por un tiempo. Sin embargo, le sugiero que deje las etiquetas ([.net/C#]) fuera del título. Déjalos permanecer en las etiquetas. Además, "Hola" y "Gracias", si bien son apropiados para un foro de discusión, no son apropiados para un sitio de preguntas y respuestas como SO. Gracias. –

+0

@John está bien. Gracias por los consejos. – gsharp

Respuesta

9

El usuario @mdma describe un poco acerca de la Programación Orientada a Aspectos. Para esto, necesitará usar una biblioteca externa (como la gran PostSharp), porque .NET no tiene mucha funcionalidad AOP. Sin embargo, .NET ya tiene un mecanismo de AOP para la seguridad basada en roles, que puede resolver parte de su problema. Mira el siguiente ejemplo de código .NET estándar:

[PrincipalPermission(SecurityAction.Demand, Role="HR")] 
public List<Employees> GetAllEmployees() 
{ 
    // do stuff 
} 

El PrincipalPermissionAttribute es parte del espacio de nombres System.Security.Permissions y es parte de .NET (desde .NET 1.0). Lo he estado usando desde hace años para implementar seguridad basada en roles en mis aplicaciones web. Lo bueno de este atributo es que el compilador .NET JIT teje todo en el fondo e incluso puedes definirlo en un nivel de clase. En ese caso, todos los miembros de ese tipo heredarán ese atributo y su configuración de seguridad.

Por supuesto que tiene sus limitaciones. Su segundo ejemplo de código no se puede implementar utilizando el atributo de seguridad basado en roles .NET. Creo que realmente no se puede solucionar algunas comprobaciones de seguridad personalizadas con este método ni llamar a una biblioteca de seguridad interna.

public Order GetMyOrder(int orderId) 
{ 
    Order o = GetOrderInternal(orderId); 
    BusinessSecurity.ValidateOrderForCurrentUser(o); 
} 

Por supuesto se puede utilizar un marco de AOP, pero aún tendría que escribir un atributo específico marco que de nuevo llamar a su propia capa de seguridad. Esto solo sería útil cuando dicho atributo reemplazara múltiples llamadas a métodos, por ejemplo cuando se tiene que poner código dentro de las instrucciones try, catch, finally. Cuando haría una llamada a un método simple, no habría mucha diferencia entre una única llamada a un método o un único atributo IMO.

Cuando va a devolver una colección de objetos y desea filtrar todos los objetos para los que el usuario actual no tiene los derechos adecuados, LINQ árboles de expresión puede ser útil:

public Order[] GetAllOrders() 
{ 
    IQueryable orders = GetAllOrdersInternal(); 
    orders = BusinessSecurity.ApplySecurityOnOrders(orders); 
    return orders.ToArray(); 
} 

static class BusinessSecurity 
{ 
    public static IQueryable<Order> ApplySecurityOnOrders(
     IQueryable<Order> orders) 
    { 
     var user = Membership.GetCurrentUser(); 

     if (user.IsInRole("Administrator")) 
     { 
      return orders; 
     } 

     return 
      from order in orders 
      where order.Customer.User.Name == user.Name 
      select order; 
    } 
} 

Cuando su O/RM admite LINQ a través de árboles de expresiones (como NHibernate, LINQ to SQL y Entity Framework) puede escribir dicho método de seguridad una vez y aplicarlo en todas partes. Por supuesto, lo bueno de esto es que la consulta a su base de datos siempre será óptima. En otras palabras, no se recuperarán más registros de los necesarios.

ACTUALIZACIÓN (años después):

que utiliza este atributo durante mucho tiempo en mi base de código, pero varios años atrás, llegó a la conclusión de que el atributo en función de AOP tiene terribles desventajas. Por ejemplo, dificulta la capacidad de prueba. Como el código de seguridad está entretejido con código normal, no puede ejecutar pruebas de unidad normales sin tener que suplantar a un usuario válido. Esto es frágil y no debería ser una preocupación de la prueba de la unidad (la prueba de la unidad misma viola el Principio de Responsabilidad Individual). Además de eso, te obliga a tirar tu base de código con ese atributo.

Por lo tanto, en lugar de usar el PrincipalPermissionAttribute, prefiero aplicar preocupaciones transversales como la seguridad al envolver el código con decorators. Esto hace que mi aplicación sea mucho más flexible y mucho más fácil de probar. He escrito varios artículos sobre esta técnica en los últimos años (por ejemplo, this one y this one).

0

Si está utilizando SOA, puede crear un Servicio de seguridad, y cada acción (método) enviará su contexto (UserId, OrderId, etc.). El Servicio de seguridad conoce las reglas de seguridad empresarial.

Esquema puede ser algo como esto

UI -> Security -> BLL -> DAL 
+0

cualquier muestra/literatura? – gsharp

+1

Acepto poner la seguridad en el nivel de aplicación y no en el nivel "Marco" como las otras respuestas. ¿Qué pasa si los roles son dinámicos? –

2

Una "mejor práctica" es la implementación de seguridad un aspecto. Esto mantiene las reglas de seguridad separadas de la lógica empresarial primaria, evitando la codificación difícil y facilitando el cambio de las reglas de seguridad en diferentes entornos.

El siguiente artículo enumera 7 formas de implementar aspectos y mantener el código por separado. Un enfoque que es simple y no cambia la interfaz lógica de negocios es usar un proxy. Esto expone la misma interfaz que tiene actualmente, pero permite una implementación alternativa, que puede decorar la implementación existente. Los requisitos de seguridad se pueden inyectar en esta interfaz, utilizando atributos de codificación fija o personalizados. El proxy intercepta llamadas de método a su capa de negocio e invoca las comprobaciones de seguridad apropiadas. La implementación de interceptación a través de proxies se describe en detalle aquí - Decouple Components by Injecting Custom Services into your Object's Invocation Chain. Otros enfoques de AOP se dan en Understanding AOP in .NET.

Aquí hay un forum post discutiendo la seguridad como un aspecto, con la implementación mediante el asesoramiento y atributos de seguridad. El resultado final es

public static class Roles 
{ 
    public const string ROLE_ADMIN = "Admin"; 
    public const string ROLE_CONTENT_MANAGER = "Content Manager"; 
} 

// business method  
[Security(Roles.ROLE_HR)] 
public List<Employee> GetAllEmployees(); 

Se puede retener el atributo directamente en su método de negocio, estrecho acoplamiento, o crear un proxy de servicio con estos atributos, por lo que los datos de seguridad se mantienen separados.

+0

La solución está demasiado ligada al marco en mi opinión (mediante métodos de decoración). Implementaría las funciones de seguridad formando parte del sistema, ya que puede parecer que está fuera de la lógica comercial. Aunque me gusta la sugerencia de proxy ... mis 2 centavos. –

+0

@Mike: los atributos son tuyos, por lo que no veo cómo esto está vinculado a ningún marco. Son un detalle de implementación: seguridad declaritiva en lugar de código de escritura. Aún puede escribir código, pero colocar el código en los objetos comerciales es lo que el OP dice que quiere evitar. Cuando los atributos se colocan en los objetos comerciales, todavía están "separados" del código, es decir, son fácilmente identificables. Este no es tanto el caso si realiza las comprobaciones de seguridad como código y lo pone en la lógica comercial. – mdma

+0

Hola, bueno, lo veo como si fuera una codificación. Es por eso que dije "marco" lo siento. ¿Qué pasa si los roles son dinámicos? Crearía objetos de seguridad como objetos "Usuario", por ejemplo, ya que son parte del sistema como cualquier otra cosa. –

Cuestiones relacionadas