2009-09-06 33 views
45

Estoy intentando agregar simple Autenticación y autorización a una aplicación ASP.NET MVC.Autenticación de formularios MVC de ASP.NET + Autorizar atributo + Roles simples

Sólo estoy tratando de virar en algunas funciones adicionales para la autenticación de formularios básico (debido a la simplicidad y la estructura de base de datos personalizada)

Suponiendo que esta es mi estructura de base de datos: usuario: nombre de usuario contraseña papel (lo ideal sería que alguna enumeración Cuerdas si es necesario en la actualidad, el usuario solamente tiene una función, pero esto podría cambiar)

Alto Nivel Problema:.. Dada la estructura de base de datos, me gustaría ser capaz de hacer lo siguiente:

  • simple inicia sesión usando autenticación de formularios
  • Decorar con mis acciones: [autorice (Roles = {MyRoles.Admin, MyRoles.Member})]
  • Use papeles en mis puntos de vista (para determinar enlaces para mostrar de alguna parciales)

Actualmente, de lo único que estoy seguro es de cómo autenticar. Después de eso estoy perdido. No estoy seguro de en qué punto tomo el rol de usuario (inicio de sesión, ¿cada autorización?). Dado que mis roles pueden no ser cadenas, no estoy seguro de cómo encajarán con el User.IsInRole().

Ahora, estoy preguntando aquí porque no he encontrado un "simple" para lograr lo que necesito. He visto múltiples ejemplos.

para la autenticación:

  • Tenemos la validación de usuario simple que comprueba la base de datos y "SetAuthCookie"
  • O sobreescribimos el proveedor de pertenencia y hacemos esto dentro de ValidateUser En cualquiera de estos, estoy no estoy seguro de cómo virar las funciones de usuario simple, para que funcionen con: HttpContext.Current.User.IsInRole ("Administrator") Además, no estoy seguro de cómo modificar esto para que funcione con mis valores enum.

de autorización, que he visto:

  • Derivado AuthorizeAttribute e implementar AuthorizeCore O OnAuthorization manejar papeles?
  • Implementando IPrincipal?

Cualquier ayuda sería muy apreciada. Sin embargo, me temo que podría necesitar muchos detalles, porque nada de lo que he buscado en Google parece encajar con lo que tengo que hacer.

Respuesta

6

Cree un AuthorizeAttribute personalizado que pueda usar sus enums en lugar de cadenas. Cuando necesite autorizar, convierta las enumeraciones en cadenas agregando el nombre de tipo de enumeración + el valor enum y use el IsInRole desde allí.

Para agregar funciones a un usuario autorizado tiene que adjuntar a la HttpApplicationAuthenticateRequest caso de que algo como el primer código en http://www.eggheadcafe.com/articles/20020906.asp (pero invierten masivamente el if anidadas en las cláusulas de guardia!).

Puede redondear las funciones de los usuarios en los formularios auth cookie o tomarlos de la base de datos cada vez.

+2

En realidad, eso es exactamente lo que terminé haciendo. Finalmente me di cuenta de que no puedes evitar lo de String si estás usando IsInRole. Así que podría tener mis enumeraciones a través de mis Controladores, pero si alguna vez necesito verificar Roles en la vista, estoy atascado con IsInRole ... ThanX – Kevin

+1

Factorizar la conversión enum -> cadena del atributo en un ayudante, utiliza el helper del atributo y crea un método de extensión html helper IsUserInRole que también usa el helper pero se puede acceder fácilmente desde la vista. – Neal

+1

También una opción es usar una clase con propiedades de cadena para los roles. 'public static class MyRoles {public const string Viewer =" Visor "; ..etc ..} '. Luego puede agregar métodos como GetAll(), GetDefault() y llamar a la función como '[Authorize (Roles = MyRoles.Viewer)]'. – Nenotlep

0

Agregue sus usuarios a la tabla "usuarios en roles". Use el procedimiento almacenado "addusertorole" (algo así) en su código para agregar a varios roles. Puede crear los roles muy simplemente en la tabla de "roles".

Sus tablas a utilizar: Usuario, UsersInRole, Roles

utilizar el construido en procedimientos almacenados para manipular esas tablas.Entonces todo lo que tienes que hacer es agregar el atributo.

Por ejemplo, puede tener un atributo "Admin" en una vista que selecciona un usuario y lo agrega a un rol. Puede usar el proceso almacenado para agregar ese usuario a la función.

+0

No me preocupo ed sobre la base de datos SQL. Puedo manejar eso por mi cuenta. Solo necesito saber "dónde". – Kevin

+0

Cuando dice "¿dónde" quiere decir dónde pone el atributo? –

106

Creo que he implementado algo similar.
Mi solución, basada en NerdDinnertutorial, está siguiendo.

Cuando se registra el usuario en, agregar código como este:

var authTicket = new FormsAuthenticationTicket(
    1,        // version 
    userName,      // user name 
    DateTime.Now,     // created 
    DateTime.Now.AddMinutes(20), // expires 
    rememberMe,     // persistent? 
    "Moderator;Admin"      // can be used to store roles 
    ); 

string encryptedTicket = FormsAuthentication.Encrypt(authTicket); 

var authCookie = new HttpCookie(FormsAuthentication.FormsCookieName, encryptedTicket); 
HttpContext.Current.Response.Cookies.Add(authCookie); 

Agregar código siguiente a Global.asax.cs:

protected void Application_AuthenticateRequest(Object sender, EventArgs e) 
{ 
    HttpCookie authCookie = Context.Request.Cookies[FormsAuthentication.FormsCookieName]; 
    if (authCookie == null || authCookie.Value == "") 
     return; 

    FormsAuthenticationTicket authTicket; 
    try 
    { 
     authTicket = FormsAuthentication.Decrypt(authCookie.Value); 
    } 
    catch 
    { 
     return; 
    } 

    // retrieve roles from UserData 
    string[] roles = authTicket.UserData.Split(';'); 

    if (Context.User != null) 
     Context.User = new GenericPrincipal(Context.User.Identity, roles); 
} 

Después de haber hecho esto, se puede use el atributo [Authorize] en su código de acción del controlador:

[Authorize(Roles="Admin")] 
public ActionResult AdminIndex() 

Háganme saber si tiene más preguntas.

+2

Si no funciona, simplemente agregue este ' ' en Web.config. – ocanal

+1

¡Justo lo que estaba buscando! Gracias –

+3

Intenté esto pero Context.User es siempre nulo para mí. Quizás necesite cambiar algo en web.config. Sin embargo, lo hice funcionar eliminando 'if (Context.User! = Null)' y cambiando la última línea en Application_AuthenticateRequest a 'Context.User = new GenericPrincipal (new GenericIdentity (authTicket.Name), roles);'. –

-9
using System; 
using System.Collections.Generic; 
using System.Linq; 
using System.Web; 
using System.Web.Mvc; 
using System.Web.Security; 
using SISWEBBSI.Models.Model; 
using SISWEBBSI.Models.Model.Entities; 
using SISWEBBSI.Models.ViewModel; 

namespace SISWEBBSI.Controllers.ActionFilter 
{ 
    [AttributeUsage(AttributeTargets.Class | AttributeTargets.Method, Inherited = true, AllowMultiple = false)] 
    public sealed class RequerAutorizacao : ActionFilterAttribute 
    { 
     public Grupo.Papeis[] Papeis = {} ; 
     public string ViewName { get; set; } 
     public ViewDataDictionary ViewDataDictionary { get; set; } 
     public AcessoNegadoViewModel AcessoNegadoViewModel { get; set; } 

     public override void OnActionExecuting(ActionExecutingContext FilterContext) 
     { 
      if (!FilterContext.HttpContext.User.Identity.IsAuthenticated) 
      { 
       string UrlSucesso = FilterContext.HttpContext.Request.Url.AbsolutePath; 
       string UrlRedirecionar = string.Format("?ReturnUrl={0}", UrlSucesso); 
       string UrlLogin = FormsAuthentication.LoginUrl + UrlRedirecionar; 
       FilterContext.HttpContext.Response.Redirect(UrlLogin, true); 
      } 
      else 
      { 
       if (Papeis.Length > 0) 
       { 
        //Papel ADMINISTRADOR sempre terá acesso quando alguma restrição de papeis for colocada. 
        int NovoTamanho = Papeis.Count() + 1; 
        Array.Resize(ref Papeis, NovoTamanho); 
        Papeis[NovoTamanho - 1] = Grupo.Papeis.ADMINISTRADOR; 
        UsuarioModel Model = new UsuarioModel(); 
        if (!Model.UsuarioExecutaPapel(FilterContext.HttpContext.User.Identity.Name, Papeis)) 
        { 
         ViewName = "AcessoNegado"; 
         String Mensagem = "Você não possui privilégios suficientes para essa operação. Você deve estar nos grupos que possuem"; 
         if(Papeis.Length == 1) 
         { 
          Mensagem = Mensagem + " o papel: <BR/>"; 
         } 
         else if (Papeis.Length > 1) 
         { 
          Mensagem = Mensagem + " os papéis: <BR/>"; 
         } 

         foreach (var papel in Papeis) 
         { 
          Mensagem = Mensagem + papel.ToString() + "<br/>"; 
         } 
         AcessoNegadoViewModel = new AcessoNegadoViewModel(); 
         AcessoNegadoViewModel.Mensagem = Mensagem; 
         ViewDataDictionary = new ViewDataDictionary(AcessoNegadoViewModel); 
         FilterContext.Result = new ViewResult { ViewName = ViewName, ViewData = ViewDataDictionary }; 
         return; 
        } 
       } 
      } 
     } 
    } 
} 
+5

Esta publicación necesita una explicación de por qué debe considerarse. –

3

he hecho algo como esto:

  • Utilice el Global.asax.cs para cargar las funciones que desea comparar en la sesión, la caché, o estado de la aplicación, o la carga sobre la marcha en el controlador ValidateUser

asigna la función [Autorizar] atributo a los controladores, que desea solicitar autorización para

[Authorize(Roles = "Admin,Tech")] 

o para permitir el acceso, por ejemplo los controladores de inicio de sesión y ValidateUser utilizan el atributo debajo

[AllowAnonymous] 

Mi Formulario de acceso

<form id="formLogin" name="formLogin" method="post" action="ValidateUser"> 
<table> 
    <tr> 
    <td> 
     <label for="txtUserName">Username: (AD username) </label> 
    </td> 
    <td> 
     <input id="txtUserName" name="txtUserName" role="textbox" type="text" /> 
    </td> 
    </tr> 
    <tr> 
    <td> 
     <label for="txtPassword">Password: </label> 
    </td> 
    <td> 
     <input id="txtPassword" name="txtPassword" role="textbox" type="password" /> 
    </td> 
    </tr> 
    <tr> 
     <td> 
     <p> 
      <input id="btnLogin" type="submit" value="LogIn" class="formbutton" /> 
     </p> 
     </td> 
    </tr> 
</table> 
     @Html.Raw("<span id='lblLoginError'>" + @errMessage + "</span>") 
</form> 

controlador de sesión y el controlador ValidateUser invocado del poste Formulario

usuario Validar es la autenticación a través de un servicio WCF que valida contra el contexto de Windows AD local para el servicio, pero puede cambiar esto a su propio mecanismo de autenticación

using System; 
using System.Collections.Generic; 
using System.Linq; 
using System.Web; 
using System.Web.Mvc; 
using System.Web.Security; 
using System.Security.Principal; 
using MyMVCProject.Extensions; 
namespace MyMVCProject.Controllers 
{ 
public class SecurityController : Controller 
{ 
    [AllowAnonymous] 
    public ActionResult Login(string returnUrl) 
    { 
     Session["LoginReturnURL"] = returnUrl; 
     Session["PageName"] = "Login"; 
     return View("Login"); 
    } 
    [AllowAnonymous] 
    public ActionResult ValidateUser() 
    { 
     Session["PageName"] = "Login"; 
     ViewResult retVal = null; 
     string loginError = string.Empty; 
     HttpContext.User = null; 

     var adClient = HttpContext.Application.GetApplicationStateWCFServiceProxyBase.ServiceProxyBase<UserOperationsReference.IUserOperations>>("ADService").Channel; 

     var username = Request.Form["txtUserName"]; 
     var password = Request.Form["txtPassword"]; 

     //check for ad domain name prefix 
     if (username.Contains(@"\")) 
      username = username.Split('\\')[1]; 

     //check for the existence of the account 
     var acctReq = new UserOperationsReference.DoesAccountExistRequest(); 
     acctReq.userName = username; 
     //account existence result 
     var accountExist = adClient.DoesAccountExist(acctReq); 
     if (!accountExist.DoesAccountExistResult) 
     { 
      //no account; inform the user 
      return View("Login", new object[] { "NO_ACCOUNT", accountExist.errorMessage }); 
     } 
     //authenticate 
     var authReq = new UserOperationsReference.AuthenticateRequest(); 
     authReq.userName = username; 
     authReq.passWord = password; 
     var authResponse = adClient.Authenticate(authReq); 
     String verifiedRoles = string.Empty; 
     //check to make sure the login was as success against the ad service endpoint 
     if (authResponse.AuthenticateResult == UserOperationsReference.DirectoryServicesEnumsUserProperties.SUCCESS) 
     { 
      Dictionary<string, string[]> siteRoles = null; 

      //get the role types and roles 
      if (HttpContext.Application["UISiteRoles"] != null) 
       siteRoles = HttpContext.Application.GetApplicationState<Dictionary<string, string[]>>("UISiteRoles"); 

      string groupResponseError = string.Empty; 
      if (siteRoles != null && siteRoles.Count > 0) 
      { 
       //get the user roles from the AD service 
       var groupsReq = new UserOperationsReference.GetUsersGroupsRequest(); 
       groupsReq.userName = username; 
       //execute the service method for getting the roles/groups 
       var groupsResponse = adClient.GetUsersGroups(groupsReq); 
       //retrieve the results 
       if (groupsResponse != null) 
       { 
        groupResponseError = groupsResponse.errorMessage; 
        var adRoles = groupsResponse.GetUsersGroupsResult; 

        if (adRoles != null) 
        { 
         //loop through the roles returned from the server 
         foreach (var adRole in adRoles) 
         { 
          //look for an admin role first 
          foreach (var roleName in siteRoles.Keys) 
          { 
           var roles = siteRoles[roleName].ToList(); 
           foreach (var role in roles) 
           { 
            if (adRole.Equals(role, StringComparison.InvariantCultureIgnoreCase)) 
            { 
             //we found a role, stop looking 
             verifiedRoles += roleName + ";"; 
             break; 
            } 
           } 
          } 
         } 
        } 
       } 
      } 
      if (String.IsNullOrEmpty(verifiedRoles)) 
      { 
       //no valid role we need to inform the user 
       return View("Login", new object[] { "NO_ACCESS_ROLE", groupResponseError }); 
      } 

      if (verifiedRoles.EndsWith(";")) 
       verifiedRoles = verifiedRoles.Remove(verifiedRoles.Length - 1, 1); 

      //all is authenticated not build the auth ticket 
      var authTicket = new FormsAuthenticationTicket(
      1,        // version 
      username,      // user name 
      DateTime.Now,     // created 
      DateTime.Now.AddMinutes(20), // expires 
      true,     // persistent? 
      verifiedRoles // can be used to store roles 
      ); 

      //encrypt the ticket before adding it to the http response 
      string encryptedTicket = FormsAuthentication.Encrypt(authTicket); 

      var authCookie = new HttpCookie(FormsAuthentication.FormsCookieName, encryptedTicket); 
      Response.Cookies.Add(authCookie); 

      Session["UserRoles"] = verifiedRoles.Split(';'); 

      //redirect to calling page 
      Response.Redirect(Session["LoginReturnURL"].ToString()); 
     } 
     else 
     { 
      retVal = View("Login", new object[] { authResponse.AuthenticateResult.ToString(), authResponse.errorMessage }); 
     } 

     return retVal; 
    } 
} 

}

usuario se autentica ahora crear la nueva identidad

protected void FormsAuthentication_OnAuthenticate(Object sender,  FormsAuthenticationEventArgs e) 
    { 
     if (FormsAuthentication.CookiesSupported == true) 
     { 
      HttpCookie authCookie = Context.Request.Cookies[FormsAuthentication.FormsCookieName]; 
      if (authCookie == null || authCookie.Value == "") 
       return; 

      FormsAuthenticationTicket authTicket = null; 
      try 
      { 
       authTicket = FormsAuthentication.Decrypt(authCookie.Value); 
      } 
      catch 
      { 
       return; 
      } 

      // retrieve roles from UserData 
      if (authTicket.UserData == null) 
       return; 

      //get username from ticket 
      string username = authTicket.Name; 

      Context.User = new GenericPrincipal(
         new System.Security.Principal.GenericIdentity(username, "MyCustomAuthTypeName"), authTicket.UserData.Split(';')); 
     } 
    } 

En mi sitio en la parte superior de mi _Layout.cshtml tengo algo como esto

{ 
    bool authedUser = false; 
    if (User != null && User.Identity.AuthenticationType == "MyCustomAuthTypeName" && User.Identity.IsAuthenticated) 
    { 
     authedUser = true; 
    } 
} 

Luego, en el cuerpo

 @{ 
     if (authedUser) 
      { 
      <span id="loggedIn_userName"> 
       <label>User Logged In: </label>@User.Identity.Name.ToUpper() 
      </span> 
      } 
      else 
      { 
      <span id="loggedIn_userName_none"> 

       <label>No User Logged In</label> 
      </span> 
      } 
     } 
Cuestiones relacionadas