2009-09-17 13 views
58

Tengo una base de datos existente con una tabla de usuarios, y estamos planeando tomar la base de datos y usarla para un nuevo sistema creado en ASP.NET MVC. Sin embargo, de lo que no estoy seguro es de si puedo o no crear un sistema de inicio de sesión que no use el controlador de cuenta incorporado o un proveedor de membresía regular para que podamos seguir usando la estructura de tabla existente.¿Es posible crear un sistema de inicio de sesión con ASP.NET MVC pero no usar MembershipProvider?

Entonces mi pregunta es, ¿sería esto posible? O incluso particularmente difícil de hacer si lo es?

¿Cuál es la forma más aceptada de hacer las cosas y la más simple?

Respuesta

81

Tenía exactamente el mismo requisito. Tenía mi propio esquema de usuario y rol y no deseaba migrar al esquema de membresía de asp.net, pero sí quería utilizar los filtros de acción ASP.NET MVC para verificar la autorización y los roles. Tuve que hacer una buena cantidad de excavaciones para averiguar exactamente qué hacer, pero al final fue relativamente fácil. Te ahorraré el problema y te contaré lo que hice.

1) Creé una clase que derivaba de System.Web.Security.MembershipProvider. MembershipProvider tiene una tonelada de métodos abstractos para todo tipo de funciones relacionadas con la autenticación, como la contraseña olvidada, cambiar la contraseña, crear un nuevo usuario, etc. Todo lo que quería era la capacidad de autenticar contra mi propio esquema. Así que mi clase contenía principalmente anulaciones vacías. Yo sólo hizo caso omiso de ValidateUser:

public override bool ValidateUser(string username, string password) 
{ 
    if (string.IsNullOrWhiteSpace(username) || 
     string.IsNullOrWhiteSpace(password)) 
     return false; 

    string hash = EncryptPassword(password); 
    User user = _repository.GetByUserName(username); 
    if (user == null) return false; 

    return user.Password == hash; 
} 

2) He creado una clase que deriva de System.Web.Security.RoleProvider. Nuevamente, tuve implementaciones vacías para todo lo que no necesitaba para crear y cambiar roles. Yo sólo hizo caso omiso de dos métodos:

public override string[] GetRolesForUser(string username) 
{ 
    User user = _repository.GetByUserName(username); 
    string[] roles = new string[user.Role.Rights.Count + 1]; 
    roles[0] = user.Role.Description; 
    int idx = 0; 
    foreach (Right right in user.Role.Rights) 
     roles[++idx] = right.Description; 
    return roles; 
} 

public override bool IsUserInRole(string username, string roleName) 
{ 
    User user = _repository.GetByUserName(username); 
    if(user!=null) 
     return user.IsInRole(roleName); 
    else 
     return false; 
} 

3) Entonces conecté estas dos clases en mi web.config:

<membership defaultProvider="FirstlookMemberProvider" userIsOnlineTimeWindow="15"> 
    <providers> 
    <clear/> 
    <add name="FirstlookMemberProvider" type="FirstlookAdmin.DomainEntities.FirstlookMemberProvider, FirstlookAdmin" /> 
    </providers> 
</membership> 
<roleManager defaultProvider="FirstlookRoleProvider" enabled="true" cacheRolesInCookie="true"> 
    <providers> 
    <clear/> 
    <add name="FirstlookRoleProvider" type="FirstlookAdmin.DomainEntities.FirstlookRoleProvider, FirstlookAdmin" /> 
    </providers> 
</roleManager> 

Eso es todo. Los filtros de acción de autorización predeterminados usarán estas clases. Aún tendrá que gestionar el inicio de sesión de la página de inicio de sesión y cerrar la sesión. Simplemente use las clases de autenticación de formularios estándar para esto como lo haría normalmente.

+1

+1. Personalizar el proveedor es una forma correcta de hacerlo. Gracias por señalar que tampoco es necesariamente mucho trabajo. –

+4

Oh, vale la pena señalar que la contraseña realmente debería ser salada con un nonce antes de hash, sin embargo. –

+0

Lamento revivir esto y molestarlo, pero ¿es posible obtener más información sobre su solución? Estoy un poco confundido sobre cómo hiciste la gestión del rol. – Ciel

10

Por supuesto que puede. Lo hice para mis proyectos ignorando por completo al proveedor de membresía.

Debe implementar su propio ActionFilter. Básicamente, interceptará el control antes de que se golpee una acción del controlador. En su interior, decide si continuar con la acción o redirigir al usuario a la página de inicio de sesión.

Para el atributo, puede definir cualquier parámetro que necesite para respaldar su modelo de autenticación/autorización.

public class AuthorizationAttribute : ActionFilterAttribute, IActionFilter 
{ 
    public MyRole UserRole { get; set; } 

    void IActionFilter.OnActionExecuting (ActionExecutedContext filterContext) 
    { 
     // Decide whether to grant access to the action or redirect away 
    } 
} 

[Authorization (UserRole = MyRole.All)] 
public class UserController : Controller 
{ 
    [Authorization (UserRole = MyRole.Admin)] 
    public ActionResult Delete() 
    { 
    } 
} 

En cuanto a las preocupaciones expresadas en los comentarios. Sí, habilitar la memoria caché de salida interferirá con la autorización. Uno solo tiene que ser consciente de eso.

Explicación del problema: ASP.NET MVC Tip #40 - Don’t Cache Pages that Require Authorization

+0

¿Puedes crear diseños personalizados? Por supuesto, las empresas varían, por lo que es posible que desee 'Role.IsAccountsAdmin' en lugar de simplemente' Role.Admin'. – Kezzer

+0

Estos son los personalizados, solo coinciden los nombres. Los actualizará en un momento para eliminar la ambigüedad. –

+0

¿Esto simplemente iría en el archivo de AccountController pregenerado o tendría que crear el mío? – LiamGu

1

Usted tiene al menos dos posibilidades

  • un atributo de filtro de acción personalizada que le proporcionará a su verificación de autorización
  • una costumbre IHttpModule que llenará toda la datos necesarios para el usuario conectado (incluidos los roles) y puede usar filtros de acción existentes

La segunda opción se puede usar también con formularios web regulares.

11

Cada vez que alguien le dice que algo relacionado con la seguridad es "fácil", casi siempre están equivocados. Hay muchas sutilezas en seguridad que los no expertos tienden a perder.

En particular, cualquier forma de autenticación que no trate explícitamente el almacenamiento en caché está inherentemente rota. Cuando se almacena en caché un resultado de acción, esto sucede dentro de ASP.NET, no necesariamente dentro de la pila ASP.NET MVC. Si examina el código fuente de AuthorizeAttribute, verá que contiene un código un poco complicado pero eficaz para garantizar que siempre se ejecute, incluso cuando el resultado de la acción se almacena en caché.

The best way, by far, to customize ASP.NET MVC authentication is to write a custom ASP.NET membership provider. No afirmaré que esto sea infalible, pero hay menos formas de tener problemas con una implementación de seguridad interrumpida en esta ruta que con otros métodos. Una ventaja sustancial de esta técnica es que puede sustituir un sistema de autorización diferente casi en cualquier momento, sin cambios en el código.

Si debe implementar un atributo de MVC personalizado, debe subtitular AuthorizeAttribute y anular AuthorizeCore, teniendo en cuenta los comentarios en el código fuente con respecto a la seguridad de la secuencia.

+0

En este momento, no tengo una forma de "establecer" las cosas, sé que tengo que usar los datos existentes y la estructura de la tabla, por lo que la forma más fácil, simple y segura de hacerlo es idealmente lo que yo ' m después ¿Es particularmente fácil escribir un proveedor de membresía personalizado? No lo he hecho antes y, a primera vista, parece un poco más difícil de lo que realmente es. – LiamGu

+1

Aquí hay un video http://www.asp.net/learn/videos/video-189.aspx y un artículo http://www.devx.com/asp/Article/29256 sobre cómo hacerlo. Es trabajo, pero no es un trabajo duro. Si esto parece demasiado trabajo, bueno, como dije, otras soluciones pueden parecer "más fáciles" porque están esencialmente rotas, con enormes brechas en seguridad, características y modularidad. –

+1

Prefiero usar una forma que no se rompa personalmente, especialmente porque esta aplicación también será accesible desde el exterior. Iré a ver qué pasa. – LiamGu

Cuestiones relacionadas