web-dev-qa-db-fra.com

Authentification ASP.NET MVC Forms + Attribut d'autorisation + Rôles simples

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

J'essaie simplement de mettre en valeur certaines fonctionnalités ajoutées à l'authentification de base des formulaires (en raison de la simplicité et de la structure de base de données personnalisée)

En supposant que c'est la structure de ma base de données: Utilisateur: rôle de mot de passe du nom d'utilisateur (idéalement une énumération. Des chaînes si besoin est. Actuellement, l'utilisateur n'a qu'un 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 pouvoir faire ce qui suit:

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

Actuellement, tout ce dont je suis vraiment sûr est de savoir comment s'authentifier. Après ça, je suis perdu. Je ne sais pas à quel moment dois-je saisir le rôle d'utilisateur (connexion, chaque autorisation?). Étant donné que mes rôles peuvent ne pas être des chaînes, je ne sais pas comment ils s'intégreront avec User.IsInRole ().

Maintenant, je demande ici parce que je n'ai pas trouvé un "simple" accomplir ce dont j'ai besoin. J'ai vu plusieurs exemples.

Pour l'authentification:

  • Nous avons une validation utilisateur simple qui vérifie la base de données et "SetAuthCookie"
  • Ou nous remplaçons le fournisseur d'adhésion et le faisons à l'intérieur de ValidateUser Dans l'un ou l'autre, je ne sais pas comment aborder mes rôles d'utilisateur simples, afin qu'ils fonctionnent avec le: HttpContext.Current.User.IsInRole ("Administrateur") De plus, je ne sais pas comment modifier cela pour fonctionner avec mes valeurs d'énumération.

Pour l'autorisation, j'ai vu:

  • Dériver AuthorizeAttribute et implémenter AuthorizeCore OR OnAuthorization pour gérer les rôles?
  • Vous implémentez IPrincipal?

Toute assistance sera grandement appréciée. Cependant, je crains d'avoir besoin de beaucoup de détails, car rien de ce que j'ai googlé ne semble correspondre à ce que je dois faire.

52
Kevin

Créez un AuthorizeAttribute personnalisé qui peut utiliser vos énumérations plutôt que des chaînes. Lorsque vous devez autoriser, convertissez les énumérations en chaînes en ajoutant le nom du type d'énumération + la valeur d'énumération et utilisez le IsInRole à partir de là.

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

Vous pouvez effectuer un aller-retour entre les rôles des utilisateurs dans le cookie d'authentification par formulaire ou les récupérer à chaque fois dans la base de données.

8
Neal

Je pense que j'ai mis en œuvre quelque chose de similaire.
Ma solution, basée sur NerdDinnertutorial , suit.

Lorsque vous connectez l'utilisateur , ajoutez du code comme ceci:

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 cela, vous pouvez utiliser [Authorize] attribut dans le code d'action de votre contrôleur:

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

S'il vous plaît laissez-moi savoir si vous avez d'autres questions.

118
yinner

J'ai fait quelque chose comme ça:

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

Attribuez l'attribut [Authorize] à vos contrôleurs, vous voulez demander une autorisation pour

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

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

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

Login Controller et ValidateUser controller invoqués depuis le post du formulaire

L'utilisateur de validation est l'authentification par l'intermédiaire d'un service WCF qui valide contre le contexte de Windows AD local au service, mais vous pouvez le changer à 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é maintenant créez 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 ça

 {
  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>
          }
        }
4
C0r3yh

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

Vos tables à utiliser: User, UsersInRole, Roles

Utilisez les processus stockés intégrés pour manipuler ces tables. Ensuite, tout ce que vous avez à faire est d'ajouter l'attribut.

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

0
Mike McClintock