Security with Session in ASP.NET MVC


On the Visual Studio, create new ASP.NET MVC Web Application project

Select Empty Template and Core Reference is MVC




In Models folder, create new entities class as below:

Create new class named Account.cs as below:

using System;
using System.Collections.Generic;
using System.Linq;
using System.Web;
using System.ComponentModel.DataAnnotations;

namespace LearnASPNETMVCWithRealApps.Models
{
    public class Account
    {
        [Display(Name = "Username")]
        public string Username
        {
            get;
            set;
        }

        [Display(Name = "Password")]
        public string Password
        {
            get;
            set;
        }

        public string[] Roles
        {
            get;
            set;
        }

    }
}

In Models folder, create new class named AccountModel.cs as below:

using System;
using System.Collections.Generic;
using System.Linq;
using System.Web;

namespace LearnASPNETMVCWithRealApps.Models
{
    public class AccountModel
    {
        private List<Account> accounts = new List<Account>();

        public AccountModel()
        {
            accounts.Add(new Account { Username = "acc1", Password = "123", Roles = new string[] { "superadmin", "admin", "employee" } });
            accounts.Add(new Account { Username = "acc2", Password = "123", Roles = new string[] { "admin", "employee" } });
            accounts.Add(new Account { Username = "acc3", Password = "123", Roles = new string[] { "employee" } });
        }

        public Account find(string username)
        {
            return accounts.Single(acc => acc.Username.Equals(username));
        }

        public Account login(string username, string password)
        {
            return accounts.Where(acc => acc.Username.Equals(username) && acc.Password.Equals(password)).FirstOrDefault();
        }

    }
}




Create Security folder in project. This folder contains classes need for security in ASP.NET MVC. Create classes: CustomPrincipal.cs, MyAuthorizeAttribute.cs and SimpleSessionPersister.cs as below:

using System;
using System.Collections.Generic;
using System.Linq;
using System.Web;
using System.Security.Principal;
using LearnASPNETMVCWithRealApps.Models;

namespace LearnASPNETMVCWithRealApps.Security
{
    public class CustomPrincipal : IPrincipal
    {
        public IIdentity Identity
        {
            get;
            set;
        }

        private Account Account;

        public CustomPrincipal(Account Account)
        {
            this.Account = Account;
            this.Identity = new GenericIdentity(Account.Username);
        }

        public bool IsInRole(string role)
        {
            var roles = role.Split(new char[] { ',' });
            return roles.Any(r => this.Account.Roles.Contains(r));
        }
    }
}
using System;
using System.Collections.Generic;
using System.Linq;
using System.Web;
using System.Web.Mvc;
using System.Web.Routing;
using LearnASPNETMVCWithRealApps.Security;
using LearnASPNETMVCWithRealApps.Models;

namespace LearnASPNETMVCWithRealApps.Security
{
    public class MyAuthorizeAttribute : AuthorizeAttribute
    {
        public override void OnAuthorization(AuthorizationContext filterContext)
        {
            if (string.IsNullOrEmpty(SimpleSessionPersister.Username))
            {
                filterContext.Result = new RedirectToRouteResult(new RouteValueDictionary(new { controller = "Error", action = "Index" }));
            }
            else
            {
                AccountModel accountModel = new AccountModel();
                CustomPrincipal customPrincipal = new CustomPrincipal(accountModel.find(SimpleSessionPersister.Username));
                if (!customPrincipal.IsInRole(Roles))
                {
                    filterContext.Result = new RedirectToRouteResult(new RouteValueDictionary(new { controller = "Error", action = "Index" }));
                }
            }
        }
    }
}
using System;
using System.Collections.Generic;
using System.Linq;
using System.Web;

namespace LearnASPNETMVCWithRealApps.Security
{
    public static class SimpleSessionPersister
    {
        static string usernameSessionvar = "username";

        public static string Username
        {
            get
            {
                if (HttpContext.Current == null) {
                    return string.Empty;
                }
                var sessionVar = HttpContext.Current.Session[usernameSessionvar];
                if (sessionVar != null) {
                    return sessionVar as string;
                }
                return null;
            }
            set
            {
                HttpContext.Current.Session[usernameSessionvar] = value;
            }
        }
    }
}




Create new folder named ViewModels, create new class named AccountViewModel.cs as below:

using System;
using System.Collections.Generic;
using System.Linq;
using System.Web;
using LearnASPNETMVCWithRealApps.Models;

namespace LearnASPNETMVCWithRealApps.ViewModels
{
    public class AccountViewModel
    {
        public Account Account
        {
            get;
            set;
        }
    }
}

In Controllers folder, create new controllers as below:

Create new controller named DemoController.cs as below:

using System;
using System.Collections.Generic;
using System.Linq;
using System.Web;
using System.Web.Mvc;
using LearnASPNETMVCWithRealApps.Security;

namespace LearnASPNETMVCWithRealApps.Controllers
{

    public class DemoController : Controller
    {
        [AllowAnonymous]
        public ActionResult Index()
        {
            return View();
        }

        [MyAuthorizeAttribute(Roles = "superadmin")]
        public ActionResult Work1()
        {
            return View("Work1");
        }

        [MyAuthorizeAttribute(Roles = "superadmin,admin")]
        public ActionResult Work2()
        {
            return View("Work2");
        }

        [MyAuthorizeAttribute(Roles = "superadmin,admin,employee")]
        public ActionResult Work3()
        {
            return View("Work3");
        }

    }
}

Create new controller named AccountController.cs as below:

using System;
using System.Collections.Generic;
using System.Linq;
using System.Web;
using System.Web.Mvc;
using LearnASPNETMVCWithRealApps.Security;
using LearnASPNETMVCWithRealApps.Models;
using LearnASPNETMVCWithRealApps.ViewModels;

namespace LearnASPNETMVCWithRealApps.Controllers
{
    public class AccountController : Controller
    {
        public ActionResult Index()
        {
            return View();
        }

        [HttpPost]
        public ActionResult Login(AccountViewModel accountViewModel)
        {
            AccountModel accountModel = new AccountModel();
            if (string.IsNullOrEmpty(accountViewModel.Account.Username) || string.IsNullOrEmpty(accountViewModel.Account.Password) || accountModel.login(accountViewModel.Account.Username, accountViewModel.Account.Password) == null)
            {
                ViewBag.Error = "Please provide your username and password correct!!!";
                return View("Index");
            }
            SimpleSessionPersister.Username = accountViewModel.Account.Username;
            return View("Welcome");
        }

        public ActionResult Logout()
        {
            SimpleSessionPersister.Username = string.Empty;
            return RedirectToAction("Index");
        }

    }
}

Create new controller named ErrorController.cs as below:

using System;
using System.Collections.Generic;
using System.Linq;
using System.Web;
using System.Web.Mvc;

namespace LearnASPNETMVCWithRealApps.Controllers
{
    public class ErrorController : Controller
    {
        public ActionResult Index()
        {
            return View();
        }
    }
}




In Views folder, create razor new views as below:

In Views/Account folder, create new views as below:

Create new view named Index.cshtml as below:

@{
    Layout = null;
}

@model LearnASPNETMVCWithRealApps.ViewModels.AccountViewModel

<!DOCTYPE html>

<html>
<head>
    <meta name="viewport" content="width=device-width" />
    <title>Index</title>
</head>
<body>

    @using (Html.BeginForm("Login", "Account", FormMethod.Post))
    {
        @ViewBag.Error
        <table cellpadding="2" cellspacing="2">
            <tr>
                <td>@Html.LabelFor(model => model.Account.Username)</td>
                <td>@Html.TextBoxFor(model => model.Account.Username)</td>
            </tr>
            <tr>
                <td>@Html.LabelFor(model => model.Account.Password)</td>
                <td>@Html.PasswordFor(model => model.Account.Password)</td>
            </tr>
            <tr>
                <td>&nbsp;</td>
                <td><input type="submit" value="Save" /></td>
            </tr>
        </table>
    }

</body>
</html>

Create new view named Welcome.cshtml as below:

@{
    Layout = null;
}

@using LearnASPNETMVCWithRealApps.Security

<!DOCTYPE html>

<html>
<head>
    <meta name="viewport" content="width=device-width" />
    <title>Welcome</title>
</head>
<body>
    <div>
        Welcome @SimpleSessionPersister.Username - <a href="@Url.Action("Logout", "Account")">Logout</a>
    </div>
</body>
</html>

In Views/Demo folder, create new views as below:

Create new view named Index.cshtml as below:

@{
    Layout = null;
}

@using LearnASPNETMVCWithRealApps.Security

<!DOCTYPE html>

<html>
<head>
    <meta name="viewport" content="width=device-width" />
    <title>Index</title>
</head>
<body>

    <h3>Index Page</h3>

</body>
</html>

Create new view named Work1.cshtml as below:

@{
    Layout = null;
}

@using LearnASPNETMVCWithRealApps.Security

<!DOCTYPE html>

<html>
<head>
    <meta name="viewport" content="width=device-width" />
    <title>Work 1</title>
</head>
<body>

    <h3>Work 1 Page</h3>
    <br />
    Welcome @SimpleSessionPersister.Username - <a href="@Url.Action("Logout", "Account")">Logout</a>

</body>
</html>

Create new view named Work2.cshtml as below:

@{
    Layout = null;
}

@using LearnASPNETMVCWithRealApps.Security

<!DOCTYPE html>

<html>
<head>
    <meta name="viewport" content="width=device-width" />
    <title>Work 2</title>
</head>
<body>

    <h3>Work 2 Page</h3>
    <br />
    Welcome @SimpleSessionPersister.Username - <a href="@Url.Action("Logout", "Account")">Logout</a>

</body>
</html>

Create new view named Work3.cshtml as below:

@{
    Layout = null;
}

@using LearnASPNETMVCWithRealApps.Security

<!DOCTYPE html>

<html>
<head>
    <meta name="viewport" content="width=device-width" />
    <title>Work 3</title>
</head>
<body>

    <h3>Work 3 Page</h3>
    <br />
    Welcome @SimpleSessionPersister.Username - <a href="@Url.Action("Logout", "Account")">Logout</a>

</body>
</html>

In Views/Error folder, create new views as below:

Create new view named Index.cshtml as below:

@{
    Layout = null;
}

<!DOCTYPE html>

<html>
<head>
    <meta name="viewport" content="width=device-width" />
    <title>Index</title>
</head>
<body>
    <h3>Access Denied</h3>
</body>
</html>




Access Index action in Account controller with following url: http://localhost:49328/Account/Index

Output

Test access Index action in Demo controller without account with url: http://localhost:49328/Demo/Index

Output

Test access Work1 action in Demo controller without account with url: http://localhost:49328/Demo/Work1

Output

Test access Work2 action in Demo controller without account with url: http://localhost:49328/Demo/Work2

Output

Test access Work3 action in Demo controller without account with url: http://localhost:49328/Demo/Work3

Output

Test login with invalid account: username is abc and password is 123

Output

Test login with valid account: username is acc2 and password is 123. This account have roles: admin and employee

Output

Use acc2 has logged access Work1 action in Demo controller with url: http://localhost:49328/Demo/Work1

Output

Use acc2 has logged access Work2 action in Demo controller with url: http://localhost:49328/Demo/Work2

Output

Use acc2 has logged access Work3 action in Demo controller with url: http://localhost:49328/Demo/Work3

Output

I recommend you refer to the books below to learn more about the knowledge in this article: