Introduction
According to the requirements of various projects, Some times we need to build unique authentication and authorization process. This post is about role based customized
authentication and authorization.
Now clear all class
files in Controllers , Models and Views folders using the
tree view of
Solution Explorer.
Then Add AccountController class
in Controller folder and at the same time add a view to
the Index method.
Now we are going to
concern about the Model section.
Add a Class called
Account in the Model folder.
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 here we defined a
string array called Roles to store the various roles that an
account can have.
After that add another
class called AccountModel in the Model folder.
public
class AccountModel {
private
List<Account> listAccounts = new List<Account>();
public AccountModel() {
listAccounts =
DBTransactions.GetAccountList();
}
public
Account FindAccount(string username) {
return listAccounts.Where(acc =>
acc.Username.Equals(username)).FirstOrDefault();
}
public Account Login (string username , string password) {
public Account Login (string username , string password) {
return listAccounts.Where(acc => acc.Username.Equals(username)
&&
acc.Password.Equals(password)).FirstOrDefault();
}
}
}
Note that
, DBTransactions is a Static class implemented by me in the project
to get database data from the SQL Server Managemet Studio. I have used
Entity Framework to make it easier to access table and stored procedures in the
database. Make sure you have added the correct connection string to the
database you are using in the web.config file.
In here we listed down
the available accounts in the database , Find the Account which is match with
the entered user name and the password by the user.
Now we can go to the RouteConfig class
in the App_Start folder and change the configuration as
follows.
public static void RegisterRoutes(RouteCollection routes)
{
routes.IgnoreRoute("{resource}.axd/{*pathInfo}");
routes.MapRoute(
name:
"Default",
url:
"{controller}/{action}/{id}",
defaults:
new { controller = "Account", action = "Index", id =
UrlParameter.Optional }
);
}
Then add a new folder
called ViewModels in the folder structure in Solution
Explorer.
After that add a class
called AccountViewModel in the ViewModel folder.
public class
AccountViewModel
{
public Account Account {get ; set ; }
}
We are going to use this
ViewModel for the Index View of AccountController.
This is my View which
allows user to login the system.
@model SessionSecurity.ViewModel.AccountViewModel
<!DOCTYPE html>
<html>
<head>
<meta name="viewport"
content="width=device-width" />
<title>Index </title>
</head>
<body>
<h6>@ViewBag.Error</h6>
@using (Html.BeginForm("Login",
"Account")) {
<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> </td>
<td><input type="submit"
name="login" value="Login"> </td>
</tr>
</table>
}
</body>
</html>
Then we need to develop the controller method relevant to Login and LogOut Actions.
In AccountController class I have implemented the Login and LogOut Actions as follows.
[HttpPost]
public ActionResult Login
(AccountViewModel avm)
{
AccountModel am = new
AccountModel();
if(string.IsNullOrEmpty(avm.Account.Username) ||
string.IsNullOrEmpty(avm.Account.Password) ||
am.Login(avm.Account.Username , avm.Account.Password) == null)
{
ViewBag.Error = "Account is Invalid";
return
View("Index");
}
SessionPersister.Username
= avm.Account.Username;
return
RedirectToAction("Index" , "DashBoard");
}
[HttpPost]
public ActionResult
Logout(AccountViewModel avm)
{
SessionPersister.Username
= string.Empty;
return
RedirectToAction("Index");
}
Note that we need to specify the view that the system routes after a success login action.
In the configuration
file, (web.config) you have to enter the following code indicating the login
page's URL.
<system.web>
<authentication mode="Forms">
<forms
loginUrl="~/Account/Index"></forms>
</authentication>
<compilation debug="true"
targetFramework="4.6" />
<httpRuntime targetFramework="4.6" />
</system.web>
Then we need to
implement the security classes that provide the custom authentication and
authorization to the Controllers.
For this, I have added a
new Folder to the project and named it as Security. After that I have added
three classes called CustomPrincipal which is inherited by the
interface called IPrincipal , SessionPersister and CustomAuthorizeAttribute which
is inherited by the AuthorizeAttribute class.
CustomPrincipal Class
public class CustomPrincipal : IPrincipal
{
private Account Account;
public CustomPrincipal(Account account)
{
this.Account = account;
this.Identity = new
GenericIdentity(account.Username);
}
public IIdentity Identity
{
get;
set;
}
public bool IsInRole(string role)
{
var roles =
role.Split(new char[] { ',' }).ToList();
bool isinRole = false;
var myroles = this.Account.Roles.ToList();
foreach (var i in roles)
{
if
(myroles.Contains(i.ToString().Trim()))
{
isinRole = true;
break;
}
}
return isinRole;
}
}
SessionPersister Class
public static class SessionPersister
{
static SessionSecurityEntities db =
new SessionSecurityEntities();
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;
}
}
}
AuthorizeAttribute
Class
public class CustomAuthorizeAttribute : AuthorizeAttribute
{
public override void
OnAuthorization(AuthorizationContext filterContext)
{
if
(string.IsNullOrEmpty(SessionPersister.Username))
filterContext.Result = new RedirectToRouteResult(new RouteValueDictionary
(new { controller = "Account", action = "Index" }));
else
{
AccountModel am = new AccountModel();
CustomPrincipal customPrincipal = new CustomPrincipal(am.FindAccount
(SessionPersister.Username));
if
(!customPrincipal.IsInRole(Roles))
filterContext.Result = new RedirectToRouteResult(new
RouteValueDictionary
(new RouteValueDictionary(new { Controller =
"AccessDenied", action = "Index" })));
}
}
}
Granting the Permission
Now we have already
implemented the Security part of the accessing the project. Now let's see how
to use it with the application.
Let's create another
controller called Test for testing the privileges. And later
you can apply this with any Controller class or with any Action.
public class TestController : Controller
{
[AllowAnonymous]
public ActionResult Index()
{
return View();
}
[CustomAuthorize(Roles = "SuperAdmin")]
public ActionResult Testing1()
{
return View("Testing1");
}
[CustomAuthorize(Roles = "SuperAdmin, Admin")]
public ActionResult Testing2()
{
return View("Testing2");
}
[CustomAuthorize(Roles = "SuperAdmin , Admin, Employee")]
public ActionResult Testing3()
{
return View("Testing3");
}
}
(With this Controller class you need to Add Views to each and every action.)
At the same time we have to implement another controller called AccessDenied and add a view to it too. Then this View will be loaded for the unauthorized accesses of the users.
I hope this post will be
helpful.
Byee :)
Thank you
ReplyDelete