Passing User Info to MVC Controller

2019-08-25 09:31发布

问题:

I am looking at various methods to implement Authentication in my MVC3 app. I would like to use my own code to do the authentication – something similar to Is it possible to create a Logon System with ASP.NET MVC but not use the MembershipProvider? (I know that there are other methods.) I would like to know once I authenticate a user using one of these methods, how do I get the user information to the Controller constructor. (By user information I mean username or userID).

One of the options I considered is putting that info into the Session. This works, but it is difficult to test since I get the Session out of the Context which does not exist during the test.

I would appreciate any ideas for how to pass user info to the controller constructor.

回答1:

No. Do not use Session for authentication. It's less secure, and unstable (sessions can be destroyed at will by the server).

In MVC, you don't need to use membership at all, but.. and I will make a point of emphasizing the but... Doing authentication correctly is not a trivial task. It's very very very easy to get it wrong and not even realize it. Even if you know what you're doing. It's something that should be heavily analyzed, tested, verified, and re-analyzed.

I would suggest, if you don't want to extend this effort, you should probably just use the default providers (there are several you can choose from).

But in any event, if you are determined to do it yourself, all you need is some way to verify the user. MVC does not integrate with the membership provider like WebForms does. It uses it for convenience. If you look in the default AccountController that is generated for you if you create an Internet project, all it does is call Membership.VerifyUser().

The truly important thing is the Authentication cookie system, which MS provides in the form of the FormsAuthentication class. I would VERY strongly recommend using this for the cookie management, unless you REALLY REALLY REALLY know what you are doing.

Just look in the AccountController, and it should be very obvious how this works. FormsAuthentication is the part that integrates into the app and tells asp.net that the user has already been authenticated. It uses a secure, encrypted cookie, and it's well designed (it even allows you to store your own additional data in an encrypted format).

Forms Authentication is a set of classes that work together to provide a transparent authentication mechanism, and is integrated into MVC and Asp.net WebForms. They are basically an implementation of the IPrincipal and IIdentity system, which is integral to asp.net (if you type User.IsAuthenticated this uses the IPrincipal interface).



回答2:

In my original post I was looking at passing User Info to the Controller constructor. I did not want the Controller to depend on HttpContext, because this would make it difficult to test.

While I thank Mystere Man for his solution, I hope the following alternate solution would help someone. I have a small project (about a dozen controllers) so it is not too bad.

I basically created my custom ControllerFactory inheriting from DefaultControllerFactory:

public class MyCustomControllerFactory : DefaultControllerFactory
    {
        public MyCustomControllerFactory ()
        {
        }


        protected override IController GetControllerInstance(RequestContext requestContext, Type controllerType)
        {
            if (controllerType == null)
            {
                return null;
            }
            else
            {
                //Example of User Info - Customer ID
                string customerIDStr =  requestContext.HttpContext.Session["CustomerID"].ToString();
                int customerID = Int32.Parse(customerIDStr);

                //Now we create each of the Controllers manually
                if (controllerType == typeof(MyFirstController))
                {
                    return new MyFirstController(customerID);
                }
                else if (controllerType == typeof(MySecondController))
                {
                    return new MySecondController(customerID);
                }
                //Add/Create Controllers similarly
                else //For all normal Controllers i.e. with no Arguments
                {
                    return base.GetControllerInstance(requestContext, controllerType);
                }
            }
        }
    }

I then set the ControllerFactory in the Global.asax.cs Application_Start() method.

protected void Application_Start()
        {
            AreaRegistration.RegisterAllAreas();

            RegisterGlobalFilters(GlobalFilters.Filters);
            RegisterRoutes(RouteTable.Routes);

            ControllerBuilder.Current.SetControllerFactory(new MyCustomControllerFactory ());
        }

P.S. I looked into using DI Containers like Ninject, but I think they were too complicated for my current project. I would look at them in a few months when it really makes sense to use them.