ASP.NET MVC Forms Authentication + Authorize Attribute + Simple Roles

I'm trying to add simple Authentication and Authorization to an ASP.NET MVC application.

I'm just trying to tack on some added functionality to the basic Forms Authentication (due to simplicity and custom database structure)

Assuming this is my database structure: User: username password role (ideally some enum. Strings if need be. Currently, user only has ONE role, but this might change)

High Level Problem: Given the above database structure, I would like to be able to do the following:

  • Simple Login using Forms Authentication
  • Decorate my actions with: [Authorize(Roles={ MyRoles.Admin, MyRoles.Member})]
  • Use roles in my Views (to determine links to display in some partials)
  • Currently, all I'm really sure of is how to Authenticate. After that I'm lost. I'm not sure at which point do I grab the user role (login, every authorization?). Since my roles may not be strings, I'm not sure how they will fit in with the User.IsInRole().

    Now, I'm asking here because I haven't found a "simple" accomplish what I need. I have seen multiple examples.

    For Authentication:

  • We have simple user validation that checks the database and "SetAuthCookie"
  • Or we override the Membership provider and do this inside of ValidateUser In either of these, I'm not sure how to tack on my simple user Roles, so that they work with the: HttpContext.Current.User.IsInRole("Administrator") Furthermore, I'm not sure how to modify this to work with my enum values.
  • For Authorization, I've seen:

  • Deriving AuthorizeAttribute and implementing AuthorizeCore OR OnAuthorization to handle roles?
  • Implementing IPrincipal?
  • Any assistance would be greatly appreciated. However, I fear I may need a lot of detail, because none of what I've Googled seems to fit with what I need to do.


    Build a custom AuthorizeAttribute that can use your enums rather than strings. When you need to authorise, convert the enums into strings by appending the enum type name + the enum value and use the IsInRole from there.

    To add roles into an authorised user you need to attach to the HttpApplication AuthenticateRequest event something like the first code in http://www.eggheadcafe.com/articles/20020906.asp ( but invert the massively nested if statements into guard clauses!).

    You can round-trip the users roles in the forms auth cookie or grab them from the database each time.


    I think I've implemented something similar.
    My solution, based on NerdDinner tutorial, is following.

    When you sign the user in , add code like this:

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

    Add following code to 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);
    }
    

    After you've done this, you can use [Authorize] attribute in your controller action code:

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

    Please let me know if you have further questions.


    I did something like this:

  • Use the Global.asax.cs to load the roles you want to compare in session,cache, or application state, or load them on the fly on the ValidateUser controller
  • Assign the [Authorize] attribute to your controllers, you want to require authorization for

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

    or to allow access, for example the Login and ValidateUser controllers use the below attribute

     [AllowAnonymous] 
    

    My Login Form

    <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 and ValidateUser controller invoked from the Form post

    Validate user is authentication via a WCF service that validates against the Windows AD Context local to the service, but you can change this to your own authentication mechanism

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

    }

    User is authenticated now create the new Identity

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

    On my site at the the top of my _Layout.cshtml I have something like this

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

    Then in the body

            @{
             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>
              }
            }
    
    链接地址: http://www.djcxy.com/p/90162.html

    上一篇: MVC动态页面权限使用授权属性?

    下一篇: ASP.NET MVC表单身份验证+授权属性+简单角色