Using the functionality of the MVC4 framework to authorize users and using the role model of access to the site

  • Tutorial
Greetings.
Today I would like to tell in a very small lesson (the level is more likely for very beginners), how to quickly and easily configure user authentication , as well as authorization when they access some functionality on your site, using the standard tools of the MVC framework (4).

Introductory

I’m writing a simple personal website for accounting and maintaining expenses, income, reminders for periodic payments (utilities, loans, school, etc.) + analytics (mostly diagrams), because the functionality of Google Docs stopped arranging me and my wife.
Accordingly, the question arose of how to close the information , in this case the financial condition of the family from prying eyes, under authentication and also distribute access roles (authorization) - what can a wife, child, anonymous users, and what can an administrator head of a family do.

UPD: described ways to create users, roles in a more correct way (no need to go directly into the database)
The code showing the menu should be translated into a more correct form, corresponding to the ideology of MVC, since the current code is far from exemplary and written quickly, for demonstration, I'm working on it.


A small preface - The reasons that prompted me to write this article and a few notes for beginners ">

The project was created as part of the study of C #, MVC4 - I am a beginner.
I spent several evenings searching, fussing with user providers and their settings, until I realized that I did not need all this code at this stage. The consequence was the rewriting of the article according to the principle once driven into my head - the less changes are made to any object, whether it be a configuration file, document or code at the current level of my knowledge or ideas, the easier it will be. Perhaps I missed some important nuances (I am a beginner), so I will be glad both to criticism and to prompts from the audience.

I believe that the user has already created at least one simple site on this framework, for example, using the basic instructions for creating a simple catalog of your films on the MVC4 framework (it took me about 20 minutes).
Or studying MVC in the second,a very good and more complicated instruction for creating an online store selling music albums (it concerns MVC3, but, nevertheless, I recommend that you start studying MVC with this instruction).

In the process of studying the second instruction, I came across problems related to the fact that some things in MVC4 have changed compared to MVC3, and I consider the old controller code from the previous framework model and model to be a bad idea, so I decided to deal with this problem.

Prerequisite:

When creating a new MVC4 project, by default, at the top, on the right, when you open the site, there is a small menu of two items - “Registration” and “Sign In”.


Technical task:

“Activate” and run on our site a role-based access model with the distribution of functionality.

By default, everything that we need is already there and it works, thanks to the developers, but we will have to add something on our own.

Working environment:

I have the usual Russian-language versions of Visual Studio 2012 Express, .Net 4.5, SQL 2012 and MVC4 (as well as TFS2012 Express. All this lives on Windows 2008R2), if you install English localization of the name, menu items and other interface elements will be called differently, so I abstracted as much as possible from screenshots of the screen.

The solution of the problem



Storage Preparation

I prefer to separate application data from authorization, so I created a separate users database,

  1. In the App_Data directory, you need to create a new, empty database, let's call it “users” to identify its contents.
  2. In our web application, we need to describe the connection to our new database, for this we need to open the web.config file, which is located in the root directory of our application, find (usually at the very beginning of the file), an item that describes connections to data sources. (I took the config from an already working site, so you will only have the name of the connection DefaultConnection the correct and matching point).

    A little more detailed:
    In this list of connections, we have the default connection “DefaultConnection”, we will only use it to store user information, so we will not change this, all application data will be stored in another database, payDBContext.
    In the DefaultConnection settings, we change:
    1. The initial directory “Initial Catalog = aspnet-MyMoney-2132141343”, this is the name of the database at the time of attaching the database to the database server, therefore, if several databases work with this same name, there may be ambiguities
    2. point “AttachDBFilename = | DataDirectory | \ users.mdf”, the name of the database file where information about users and roles will be stored. The name of the database that we created in the App_Data directory is “users.mdf”.

  3. Now you can start the site, log in and register, for example, two users Admin and User
  4. Then in the database explorer right in the studio we open the structure of our database

    and get something like this
  5. To add roles, you can add the following code to accountmodel.cs:
        public class UsersContext : DbContext
        {
            public UsersContext()
                : base("users")
            {
            }
            public DbSet UserProfiles { get; set; }
            public DbSet webpages_Roles { get; set; } // добавляем таблицу ролей
            }
    // описание таблицы ролей
        [Table("webpages_Roles")]
        public class webpages_Roles
        {
            [Key]
            [DatabaseGeneratedAttribute(DatabaseGeneratedOption.Identity)]
            public int RoleId { get; set; }
            [Display(Name = "Имя роли")]
            public string RoleName { get; set; }
        }
    

    Then, after reassembling the project, we can create a roles controller (do not forget to close it for users with an administrative role).


    An example of adding users is better to look at the Account controller, Register methods and modify it to add a user to one or several roles using Roles.AddUserToRole ( ), for example, by closing for the administrator role, or add your controller. which will add the user to the role.


I have several controllers responsible for working with financial information, so in the description of each controller (you can close or vice versa, open individual methods), you need to insert the appropriate access setting:
[Authorize(Roles = "Admin")]

namespace MyMoney.Controllers
{
    [Authorize(Roles = "Admin")]
    public class catController : Controller
    {

Or insert such a line to enter the methods of several roles
[Authorize(Roles = "Admin, User")]

Now I want to change the menu list a little depending on the role of the user.
  1. In the directory / Views / Shared, I create a partial view (aka Partial View) with the name "_Menu"
    the source code, by the way, who will tell you how to optimize it better, otherwise it pulled
    @{
        var menus = new[]
                    {
                       new { LinkText="На главную", ActionName="Index",ControllerName="Home",Roles="All" },
                       new { LinkText="О себе", ActionName="About",ControllerName="Home",Roles="All" },
                       new { LinkText="Контакты", ActionName="Contact",ControllerName="Home",Roles="All" },
                       new { LinkText="Финансы", ActionName="Index",ControllerName="payments",Roles="Admin,User" },
                    };
    }
    


  2. Now we need to connect it to the markup /Views/Shared/_Layout.cshtml

    We are looking for this code in the file _Layout.cshtml
    @Html.Partial("_LoginPartial")


    and change the block

    on the

  3. I have one controller with a short name using one view as a menu, here is the view code.
    Sketched over quick to show work with roles.
    submission code
    @{
        var menus = new[]
                    {
                       new { LinkText="Home", ActionName="Index",ControllerName="Home",Roles="All"  },
                       new { LinkText="About", ActionName="About",ControllerName="Home",Roles="Anonymous"  },
                       new { LinkText="Contact", ActionName="Contact",ControllerName="Home",Roles="Anonymous"  },
                       new { LinkText="Добавить платёж", ActionName="Create",ControllerName="pay",Roles="Admin,User"  },
                       new { LinkText="Просмотр платежей", ActionName="Index",ControllerName="pay",Roles="Admin,User"  },
                       new { LinkText="Добавить категорию", ActionName="Create",ControllerName="cat",Roles="Admin"  },
                       new { LinkText="Просмотр категорий", ActionName="Index",ControllerName="cat",Roles="Admin,User"  },
                       new { LinkText="Добавить пользователя", ActionName="Create",ControllerName="user",Roles="Admin"  },
                       new { LinkText="Просмотр пользователей", ActionName="Index",ControllerName="user",Roles="Admin"  },
                       new { LinkText="Добавить тип", ActionName="Create",ControllerName="type",Roles="Admin"  },
                       new { LinkText="Просмотр типов", ActionName="Index",ControllerName="type",Roles="Admin"  },
                       //new { LinkText="", ActionName="",ControllerName="",Roles="Administrator"  },
                    };
    }
    @{
        ViewBag.Title = "Управление финансами";
    }
    

    Управление финансами

    Вы: @User.Identity.Name

    Вы входите в группы:

    @{ foreach (string item in Roles.GetRolesForUser()) {
  4. @item
  5. } }
      @if (HttpContext.Current.User.Identity.IsAuthenticated) { String[] roles = Roles.GetRolesForUser(); var links = from item in menus where item.Roles.Split(new String[] { "," }, StringSplitOptions.RemoveEmptyEntries) .Any(x => roles.Contains(x) || x == "All") select item; foreach (var link in links) { @:
    • @Html.ActionLink(link.LinkText, link.ActionName, link.ControllerName)
    •     } } else { var links = from item in menus where item.Roles.Split(new String[] { "," }, StringSplitOptions.RemoveEmptyEntries) .Any(x => new String[] { "All", "Anonymous" }.Contains(x)) select item; foreach (var link in links) { @:
    • @Html.ActionLink(link.LinkText, link.ActionName, link.ControllerName)
    •      } }




Now you need to clean, rebuild the application and you can run it. If, knowing the link to one of the controllers that are closed for anonymous login, try to log in - get an authorization form with a subsequent return to the page that was the authorization authorizer.
It looks like this:

Anonymous login



Login with an administrative role



Login with a user role


Last remarks
What I’m thinking about right now is that you need to link the receipt of the data of the current authorized user in your finance database, as Now in Google Docks you have to choose with your own hands who was the initiator of the expense / income - and entering information twice is silly. And ideally, get authorization from a Windows session if the browser is IE.

Yes, for some fragments of the code, I, as a beginner, have to beat my hands painfully, so I will be happy to accept corrections and improvements to this code.

Thank you for your attention, I hope I helped someone.

Also popular now: