55 votes

Authentification ASP.NET MVC Forms + Attribut Authorize + Rôles simples

J'essaie d'ajouter simple Authentification et autorisation d'une application ASP.NET MVC.

J'essaie simplement d'ajouter quelques fonctionnalités à l'authentification de base des formulaires (en raison de la simplicité et de la structure personnalisée de la base de données).

En supposant que c'est la structure de ma base de données : Utilisateur : nom d'utilisateur mot de passe rôle (idéalement un enum. Des chaînes de caractères si nécessaire. Actuellement, l'utilisateur n'a qu'UN seul rôle, mais cela pourrait changer)

Problème de haut niveau : Compte tenu de la structure de la base de données ci-dessus, je voudrais être en mesure de faire ce qui suit :

  • Connexion simple à l'aide de l'authentification par formulaire
  • Décorer mes actions avec : [Authorize(Roles={ MyRoles.Admin, MyRoles.Member})]
  • Utiliser les rôles dans mes vues (pour déterminer les liens à afficher dans certains partiels)

Actuellement, tout ce dont je suis vraiment sûr, c'est comment s'authentifier. Après cela, je suis perdu. Je ne sais pas à quel moment je dois saisir le rôle de l'utilisateur (connexion, chaque autorisation ?). Comme mes rôles peuvent ne pas être des chaînes de caractères, je ne sais pas comment ils vont s'intégrer à la fonction User.IsInRole().

Maintenant, je demande ici parce que je n'ai pas trouvé un "simple" accomplir ce que je dois. J'ai vu de multiples exemples.

Pour l'authentification :

  • Nous avons une simple validation de l'utilisateur qui vérifie la base de données et "SetAuthCookie".
  • Ou alors, on surcharge le fournisseur d'adhésion et on fait ceci à l'intérieur de ValidateUser. Dans l'un ou l'autre de ces cas, je ne suis pas sûr de savoir comment ajouter mes rôles d'utilisateur simples pour qu'ils fonctionnent avec le fournisseur : HttpContext.Current.User.IsInRole("Administrator") De plus, je ne suis pas sûr de savoir comment modifier ceci pour qu'il fonctionne avec mes valeurs d'énumération.

Pour l'autorisation, j'ai vu :

  • Dériver AuthorizeAttribute et implémenter AuthorizeCore ou OnAuthorization pour gérer les rôles ?
  • Mise en œuvre de IPrincipal ?

Toute aide serait grandement appréciée. Cependant, je crains d'avoir besoin de beaucoup de détails, car rien de ce que j'ai cherché sur Google ne semble correspondre à ce que je dois faire.

121voto

yinner Points 721

Je crois que j'ai mis en place quelque chose de similaire.
Ma solution, basée sur NerdDinner tutoriel est en train de suivre.

Lorsque vous signez l'utilisateur ajoutez un code comme celui-ci :

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);
System.Web.HttpContext.Current.Response.Cookies.Add(authCookie);

Ajoutez le code suivant à 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);
}

Après avoir fait ça, vous pouvez utiliser [Authorize] dans le code d'action de votre contrôleur :

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

N'hésitez pas à me contacter si vous avez d'autres questions.

3 votes

Si cela ne fonctionne pas pour vous, ajoutez simplement ceci <appSettings> <add key="enableSimpleMembership" value="false" /> </appSettings> dans Web.config.

1 votes

Juste ce que je cherchais ! Merci

4 votes

J'ai essayé mais Context.User est toujours nul pour moi. Peut-être que je dois changer quelque chose dans le web.config. Cependant, j'ai réussi à le faire fonctionner en enlevant 'if (Context.User != null)' et en changeant la dernière ligne dans Application_AuthenticateRequest en 'Context.User = new GenericPrincipal(new GenericIdentity(authTicket.Name), roles);'.

8voto

Neal Points 2286

Construire une AuthorizeAttribute qui peut utiliser vos enums plutôt que des chaînes de caractères. Lorsque vous avez besoin d'une autorisation, convertissez les enums en chaînes de caractères en ajoutant le nom du type d'enum + la valeur de l'enum et utilisez la fonction IsInRole à partir de là.

Pour ajouter des rôles à un utilisateur autorisé, vous devez vous attacher à l'onglet HttpApplication AuthenticateRequest quelque chose comme le premier code dans http://www.eggheadcafe.com/articles/20020906.asp ( mais inversez les instructions if massivement imbriquées en clauses de garde !)

Vous pouvez faire circuler les rôles des utilisateurs dans le cookie d'authentification des formulaires ou les récupérer à chaque fois dans la base de données.

2 votes

En fait, c'est exactement ce que j'ai fini par faire. J'ai finalement réalisé que vous ne pouvez pas contourner le truc de la chaîne si vous utilisez IsInRole. Je pourrais donc avoir mes enums dans tous mes contrôleurs, mais si jamais j'ai besoin de vérifier les rôles dans la vue, je suis coincé avec IsInRole... Merci

1 votes

Transformez la conversion enum -> string de l'attribut en une aide, utilisez l'aide de l'attribut et créez une méthode d'extension de l'aide html IsUserInRole qui utilise également l'aide mais est facilement accessible depuis la vue.

1 votes

Une autre option consiste à utiliser une classe avec des propriétés de type chaîne pour les rôles. public static class MyRoles { public const string Viewer = "Viewer"; ..etc.. } . Vous pouvez ensuite ajouter des méthodes comme GetAll(), GetDefault() et appeler le rôle comme suit [Authorize(Roles=MyRoles.Viewer)] .

4voto

DynamicLynk Points 86

J'ai fait quelque chose comme ça :

  • Utilisez le fichier Global.asax.cs pour charger les rôles que vous souhaitez comparer dans l'état de la session, du cache ou de l'application, ou chargez-les à la volée dans le contrôleur ValidateUser.

Attribuez l'attribut [Authorize] à vos contrôleurs, vous voulez exiger l'autorisation pour les éléments suivants

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

ou pour autoriser l'accès, par exemple les contrôleurs Login et ValidateUser utilisent l'attribut suivant

 [AllowAnonymous] 

Mon formulaire de connexion

<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>

Contrôleur de connexion et contrôleur ValidateUser invoqués depuis le post du formulaire

La validation de l'utilisateur est une authentification via un service WCF qui valide contre le contexte Windows AD local au service, mais vous pouvez changer cela pour votre propre mécanisme d'authentification.

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;
    }
}

}

L'utilisateur est authentifié, créez maintenant la nouvelle identité

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(';'));
        }
    }

Sur mon site, en haut de mon _Layout.cshtml, j'ai quelque chose comme ceci

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

Puis dans le corps

        @{
         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>
          }
        }

0voto

Ajoutez vos utilisateurs à la table "users in roles". Utilisez la procédure stockée "addusertorole" (quelque chose comme ça) dans votre code pour ajouter aux différents rôles. Vous pouvez créer les rôles très simplement dans la table "roles".

Vos tables à utiliser : User, UsersInRole, Roles

Utilisez les procédures stockées intégrées pour manipuler ces tables. Il ne vous reste plus qu'à ajouter l'attribut.

Par exemple, vous pouvez avoir un attribut "Admin" sur une vue qui sélectionne un utilisateur et l'ajoute à un rôle. Vous pouvez utiliser la procédure stockée pour ajouter cet utilisateur au rôle.

-9voto

Marlon Points 19
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;
                    }
                }
            }
        }
    }
}

6 votes

Ce poste nécessite une explication sur la raison pour laquelle il doit être considéré.

Prograide.com

Prograide est une communauté de développeurs qui cherche à élargir la connaissance de la programmation au-delà de l'anglais.
Pour cela nous avons les plus grands doutes résolus en français et vous pouvez aussi poser vos propres questions ou résoudre celles des autres.

Powered by:

X