Introduction
In this article I will be discussing a very interesting topic called Web Security in MVC applications and how easy and simple it is to implement and understand. Let me share a small incident for which I came across this concept. Usually we create an MVC application and select a template, it may be internet or empty (these two are used frequently). Based on these templates, the web project is scaffolded and we get the final project structure to work on. If we select an internet application then from the default scaffolding we get a Controller for "Home" and "Account" created. ASP.NET MVC by default provides the authentication using the Web Security. I tried to customize the way authentication was done and then realized this is so simple and easy, since it reduces so many lines of our codes. Let's discuss more on this with a small Demo.
Let's Play
Let's briefly become familiar wiht Web Security! According to MSDN, it provides security and authentication to ASP.Net web applications with many more features like user account creation, Login User and Log off, Reset & Change word, using its in-built functions. It internally also does the word hashing that is one of the main concerns during the creation of a user account. The various properties and methods are well explained in the MSDN link given above. This is usually called a Helper class that is present in the namespace "System.WebMatrix.WebData". When an internet template is chosen we would find the "Dynamic Link Library" , WebMatrix.WebData.dll. Let's create a new project in Visual Studio. Here I create a new ASP.Net Web Project and name it "WebSecurityDemo" and after clicking on OK the final creation of the project in Visual Studio looks as in the following:
As we can see in the preceding image, the project is scaffolded based on the internet template in MVC. This by default creates the Home controller and Account controller with many kinds of authentication and security. This default web application lacks any coding but gives the user options to Register, Login, Log off and OWIN authentication using Facebook, Twitter, G+ ( This requires some modification that I will be discussing later). But how does this happen? This is done by the Authorize attribute provided by MVC that is one of the best assets of MVC. Just specifying an attribute like "[Authorize]" that restricts the user access to the methods/actions if specified at the Controller level or also can be specified at the action level, just as in the following snippet:
- [Authorize]
- [InitializeSimpleMembership]
- public class AccountController : Controller
- {
- [AllowAnonymous]
- public ActionResult Login(string returnUrl)
- {
- ViewBag.ReturnUrl = returnUrl;
- return View();
- }
- }
In the preceding snippet, as you can see, the Authorize attribute is given at the controller level that will restrict users from accessing the action methods defined inside this controller, but as you can also see another attribute called [Allow Anonymous] is provided on the action method, which means any user can access this action using the URL directly. This attribute overrides the controller level attribute here. The best part of this authorizes attribute is we can also override this and create our own custom Authorize attribute like the following:
- namespace WebSecurityDemoTest.Filters
- {
- public class WebSecurityAuthorize:AuthorizeAttribute
- {
- protected bool AuthorizeCore(HttpContextBase httpContext)
- {
- if (!httpContext.Request.IsAuthenticated)
- {
- return false;
- }
- if (HttpContext.Current.Session["SessionHelper"] == null)
- {
- return false;
- }
-
- return true;
- }
-
- protected void HandleUnauthorizedRequest(AuthorizationContext filterContext)
- {
- filterContext.Result = new RedirectResult("/");
- base.HandleUnauthorizedRequest(filterContext);
- }
-
- }
- }
As you might see here, I have created a custom authorize attribute that first checks if the current HTTP Request is authenticated or not. Since we will be using FormsAuthentication, this IsAuthenticated is initialized after Login or inside the LoginPost method like:
- FormsAuthentication.SetAuthCookie(userName,true);
The boolean value true does the trick. After successful login when the AuthCookie is set, the IsAuthenticated for the request is set to true. Now if that is not true, then it is handled by the other method, HandleUnAuthorizedRequest. When there is an unauthorize request it checks and returns the user back to the default page, the login page. Here, as you can also see I have checked for the session. Also, after the user has a successful login, I create a session and store the values as is usually done, and based on that I also check if the session is not null. If the AuthorizeCore method returns true, then the user navigates to the desired landing page. Thus, this is how a custom authorize attribute is created. Now let's discuss more about another class that is created inside the Filters folder. When the project is created, in other words <em>"InitializeSimpleMembershipAttribute.cs"</em>. This class is responsible for initializing the tables required by the Web Security to run in the database. Let's see how the class looks and explain it:
- using System;
- using System.Data.Entity;
- using System.Data.Entity.Infrastructure;
- using System.Threading;
- using System.Web.Mvc;
- using WebMatrix.WebData;
- using WebSecurityDemoTest.Models;
-
- namespace WebSecurityDemoTest.Filters
- {
- [AttributeUsage(AttributeTargets.Class | AttributeTargets.Method, AllowMultiple = false, Inherited = true)]
- public sealed class InitializeSimpleMembershipAttribute : ActionFilterAttribute
- {
- private static SimpleMembershipInitializer _initializer;
- private static object _initializerLock = new object();
- private static bool _isInitialized;
-
- public override void OnActionExecuting(ActionExecutingContext filterContext)
- {
-
- LazyInitializer.EnsureInitialized(ref _initializer, ref _isInitialized, ref _initializerLock);
- }
-
- private class SimpleMembershipInitializer
- {
- public SimpleMembershipInitializer()
- {
- Database.SetInitializer<UsersContext>(null);
-
- try
- {
- using (var context = new UsersContext())
- {
- if (!context.Database.Exists())
- {
-
- ((IObjectContextAdapter)context).ObjectContext.CreateDatabase();
- }
- }
-
- WebSecurity.InitializeDatabaseConnection("DefaultConnection", "UserProfile", "UserId", "UserName", autoCreateTables: true);
- }
- catch (Exception ex)
- {
- throw new InvalidOperationException("The ASP.NET Simple Membership database could not be initialized. For more information, please see http://go.microsoft.com/fwlink/?LinkId=256588", ex);
- }
- }
- }
- }
- }
As you can see, since this class extends itself from the ActionFilterAttribute, it is used as an attribute only, usually at the controller level as you can check in the first code snippet. The [InitializeSimpleMembershipAttribute] attribute, in simple language, ensures that before any action runs, the membership has been created at the database level. So that when a user registers, it stores the data in the system defined tables. System Defined Tables? Yes, the tables required for the web security are created once we build and run the application and the controller action is called that has the attribute defined. But how? The following piece of code snippet does the trick:
- WebSecurity.InitializeDatabaseConnection("DefaultConnection", "UserProfile", "UserId", "UserName", autoCreateTables: true);
As you can see, the method "InitializeDatabaseConnection" present in the WebSecurity class, explains itself what it does. This initializes the database connection based on the first parameter that is the database name here. Then the SQL table with the name is created, with the columns UserId as the primary key and the UserName associated with the user who registers. Now we need to add a database context to our project:
- Right-click on the Model folder. Select Add new Item:
- Then select Data and add an ADO.Net Entity Data Model with a valid name. I have named the data model WebSecurity.edmx. This creates the conceptual model for the database after we provide the connection.
- Then we need to specify the connection to the database. You can create a sample database or use an existing one. I will be using one existing one and will show how the tables for websecurity are generated. Select Generate from database.
- Then the connection needs to be set up. Choose your local server, so in the database TextBox enter ". (Dot)" which means local DB and that would list the databases present in your local. Choose anyone you wish to.
- Thus, when the connection is success and is established, we can find the connection string in the Web.Config file.
- Then since you can scroll up and see in the InitializeSimpleMembership.cs we have the DefaultConnection specified, we need to change the configuration specified as the default in the web.config connection string, that is change the Data Source =. And remove the AttachedDbFileName entirely and then specify the Initial Catalog as the database name that you have added during the db connection. Finally the connection string with the name "DefaultConnection" looks as in:
- <add name="DefaultConnection" connectionString="Data Source=.;Initial Catalog=Customer;Integrated Security=SSPI;" providerName="System.Data.SqlClient" />
- Thus, we have reached the end of the set up. Now clean and build the solution once and then run. When we run the application, we land on the Home page as the Home controller that has no authorization. And also remember one thing, the membership/websecurity tables will only be created once the InitializeSimpleMembership attribute is hit and in the preceding snippets it is hit only when the Account controller is accessed. So when we go to any action method that is Allow Anonymous as we also need to land on the page, so we click on Register.
- The following image shows the table created once the attribute is hit/executed. "dbo.webpages_Membership" stores all the information related to the user registered with the user word that is hashed and that is one way to increase security. User Id acts as the foreign key here to the UserId primary key in the UserProfile table that also contains the UserName that is asked during the registration.
- After the registration, you will find yourself logged in as the UserName entered and the data in the tables would look like:
- Thus this is how the simple attribute, internally does everything.
Now let's check for the code the method WebSecurity class implements. First is the "WebSecurity.UserLogin".
- WebSecurity.Login(model.UserName, model.word, persistCookie: model.RememberMe)
This method takes the UserName, the word and the persistCookie that is the Remember Me check box. This maps the webpages_Membership table and the UserProfile table for the combination of the userName and word. If that is satisfied or is perfectly mapped, it redirects the user to the authenticated page, else throws him back to the Login page. If the wrong word is entered then it gives the ModelState error as incorrect username and word. If Remember Me is checked, then the IsPersistent is true in the cookie that was created, then for the next time even after closing the browser we return, the cookie is persisted based on the span it has and authenticates the user without the credentials. The next method used is "WebSecurity.Changeword".
- WebSecurity.Changeword(User.Identity.Name, model.Oldword, model.Newword)
This method takes the unique username, with the old word and also the new word as the parameters. It internally matches the first old word with the user name (logged In) and then if that is satisfied then stores or replaces the old word with the new one encrypting it or hashing it. This is that simple. :) Now the other method and it is an important one, when the user is registered, how the data is saved into the table? Nice question. :) The answer is another builtin method that does the trick here. "WebSecurity.CreateUserAndAccount".
- WebSecurity.CreateUserAndAccount (model.UserName, model.word)
What this does is to take the parameter as the unique user name and the word, then adds the user name into the UserProfile table that returns the UserId and which in turn is used as the foreign key with a one-to-one relationship and hashes the word and stores it in the webpages_Membership table. This is how a user is registered so simply using WebSecurity. There is also a method called Logout that simply logs out a user, as in "WebSecurity.Logout()".
Conclusion
Thus, I discussed in this article the details of the WebSecurity class and how to use that in a MVC application. The method and the membership authentication is the same, we just need to customize to have our own hold over it. It is very easy to use and that makes the code less redundant and reusable and mainly maintainable. This follows the DRY concept, Don't Repeat Yourself. Thus this suggests using everything once, like have an Authorize attribute may it be Custom and reuse it wherever required. WebSecurity has everything defined, we just need to explore and use its methods and properties extensively to reduce the level of coding and LOC. Thus here I end my article, I hope I have at least explained something. Though I am not an expert and moreover a human, mistakes are inevitable. I will be happy to know about any mistakes you find. Suggestions and discussions are most welcome. Share and learn.