简体   繁体   中英

Roles in Authorize Attribute does not work as expected in MVC 4

(I am new to ASP.NET MVC 4 and prior to this project, I was used to working with WebForms with <authentication mode="none"> ).

I have a database in which I have specific roles against a user entity like ADMIN and SUBADMIN . I have separate controllers in my project to deal with different views for the different roles for example, AdminController and SubAdminController I can successfully redirect users to the respective views according to their roles.

My issue is that when the users have logged in they can even access pages which they are not privileged to access.This implies that may be I have not set up authorization properly.

What I have tried?

I have tried the authorized attribute with ADMIN role filter so that only admins are authorized to view the respective views like:

[Authorize(Roles="ADMIN")]
    public class AdminController : BaseController
    {
    ....
    }

This redirects the user even if he is successfully authenticated to the login page which is configured in web.config like this:

 <authentication mode="Forms">
      <forms loginUrl="~/Home/Index" timeout="2880" />
    </authentication>

Note that if I just use [Authorize] instead then it works but even SubAdmin can gain access to the Admin views.

I have tried configuring the authorization in Web.config too like this:

<location path="Admin">
    <system.web>

      <authorization>
        <allow roles="ADMIN"/> 

        <deny users="*"/> 
      </authorization>

    </system.web>
  </location>

This has lead to same results as using [Authorize(Roles="ADMIN")] .

Note that if I console out the following

User.Identity.ToGenericUserIdentity().RoleName; in my AdminController then I get ADMIN and i can use it in my action methods to re-check whether the user is actually an admin or sub admin but that is not the correct way to write it down as checks in all my action methods.

Related classes:

[Serializable]
    public class GenericUser
    {

        public bool IsInRole(string role) { return false; }



        public string RoleName { get; set; }
        public int UserID { get; set; }
        public string UserName { get; set; }
    }

    [Serializable]
    public class GenericUserIdentity : IIdentity
    {
        private GenericUser genericUser;
        private FormsAuthenticationTicket ticket;

        public GenericUserIdentity(FormsAuthenticationTicket ticket)
        {
            var serializer = new JsonSerializer();
            var reader = new JsonTextReader(new StringReader(ticket.UserData));
            genericUser = serializer.Deserialize<GenericUser>(reader);
            this.ticket = ticket;
        }

        public string AuthenticationType
        {
            get { return "Custom"; }
        }

        public bool IsAuthenticated
        {
            get { return ticket != null; }
        }

        public string Name
        {
            get { return genericUser.UserName; }
        }

        public string RoleName
        {
            get { return genericUser.RoleName; }
        }

        public int UserID
        {
            get { return genericUser.UserID; }
        }



    }

public static class IdentityExtension
{
    public static GenericUserIdentity ToGenericUserIdentity(this IIdentity identity)
    {
        var cookie = HttpContext.Current.Request.Cookies[FormsAuthentication.FormsCookieName];
        var ticketInfo = FormsAuthentication.Decrypt(cookie.Value);
        return new GenericUserIdentity(ticketInfo);
    }
}

And how do I authenticate?

In my HomeController which shows the login page on Index() action, I have set up a HttpPost Index action like this:

  public ActionResult Index()
        {
            return View();
        }
 [HttpPost]
        public ActionResult Index(string username,string password)
        {

            UserInformation userInfo = _userProvider.FindUserByName(username);

            OrderProvider orderProvider = new OrderProvider(new OrderRepository());

            if (userInfo != null && string.Equals(userInfo.Password, password,StringComparison.Ordinal))
            {
                if (userInfo.isEnabled)
                {
                    GenericUser genericUser = new GenericUser() { UserName = userInfo.Email, UserID = userInfo.UserID, RoleName = userInfo.RoleName };

                    Response.SetAuthCookie<GenericUser>(genericUser.UserName, genericUser);

                    if (userInfo.RoleName == "ADMIN")
                    {
                        return RedirectToAction("Index", "Admin");
                    }
                    else if (userInfo.RoleName == "USER")
                    {
                        return RedirectToAction("Index", "Customer");
                    }
                    else if (userInfo.RoleName == "DRIVER")
                    {
                        return RedirectToAction("Index", "Driver");
                    }
                    else if (userInfo.RoleName == "SUBADMIN")
                    {
                        return RedirectToAction("Index", "SubAdmin");
                    }
                }



            }

            ViewBag.Error = true;

            return View("Index");

        }

Edit

Finally overriding the AuthorizeCore method of my custom attribute worked for me.

public enum Role
    {
        ADMIN, SUBADMIN, USER, DRIVER
    }

    [AttributeUsage(AttributeTargets.Method | AttributeTargets.Class, Inherited = true, AllowMultiple = true)]
    public class AuthorizeUser : AuthorizeAttribute
    {
        public AuthorizeUser(params object[] roles)
        {
            if (roles.Any(r => r.GetType().BaseType != typeof(Enum)))
                throw new ArgumentException("roles");

            this.Roles = string.Join(",", roles.Select(r => Enum.GetName(r.GetType(), r)));
        }



protected override bool AuthorizeCore(HttpContextBase httpContext)
        {
            var cookie = HttpContext.Current.Request.Cookies[FormsAuthentication.FormsCookieName];
            var ticketInfo = FormsAuthentication.Decrypt(cookie.Value);

            GenericUserIdentity iden = new GenericUserIdentity(ticketInfo);
            if (Roles.Contains(iden.RoleName))
                return true;
            else
                return false;
        }
        }

The attribute [Authorize(Roles="ADMIN")] only works for the built-in Roles.
You have your own GenericUser.RoleName , that won't be used here.

You could write your own MyAuthorize attribute but first ask yourself very seriously why you are re-inventing the wheel here. The Identity and Membership frameworks are available and tested. When I see a string.Equals(userInfo.Password, password) I very much doubt the security of your implementation.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM