原文:Forms Authentication in ASP.NET MVC 4

Contents:

  1. Introduction
  2. Implement a custom membership provider
  3. Implement a custom role provider
  4. Implement a custom user principal and identity
  5. Implement a custom authorization filter
  6. Summary

1. Introduction 

For adding authorization and authentication features to an ASP.NET MVC site we will be using the same approach as for a classic Web Forms project. The classes that stay at the base of the ASP.NET security model can be used in both MVC and Web Forms projects. The authentication happens like in this image:

  1. The login page collects the user credentials and then calls the Membership class in order to validate them.
  2. The Membership class uses the web.config to determine what MembershipProvider to use.
  3. In the end the Membership class calls the ValiadateUser method of the membership provider that was determined in step 2. The ValidateUser method verifies if the specified username and password exist and are valid.

The MembershipProvider acts like a mediator between the ASP.NET authentication system and the collection of users. It defines methods for validating the user credentials, for creating new users, modifying the user password and a lot of other user account related operations. For more information here is an introduction to membership in MSDN. So, it provides a way to decouple the data source where the user information is stored (e.g. DB, active directory) from the authentication system so that the authentication will work in the same way no matter where the user information is stored.

Microsoft provides out of the box implementations for ActiveDirectoryMembershipProvider and SqlMembershipProvider but we can also create our own custom implementation by inheriting from the MembershipProvider class and implementing the methods that we need.
Note: if there is a method that you don’t need just leave an empty implementation because it will not be invoked except if you’ll call it explicitly from the code.

In a similar way works also the authorization:

  1. We use the AuthorizeAttribute inside the controller classes to mark the action methods that can be invoked only if the user is authenticated and/or has a given role. Then the AuthorizeAttribute uses the Roles class to check if the currently logged user has the required role.
  2. The Roles class uses the web.config to understand what RoleProvider to use.
  3. The RoleProvider is an abstract class that defines the basic methods that all role providers will have. We can use the supplied role providers (e.g. SqlRoleProvider) that are included with the .NET Framework, or we can implement our own custom provider.

2. Implement a custom membership provider 

We will continue the demo application we used throughout the previous ASP.NET MVC 4 tutorials and we will add a login page that uses a custom membership provider to authenticate the users. You can download the source code for the demo application (that we are using here as a starting point) from CodePlex.

First we will create a custom membership provider that inherits from the MembershipProvider class.

using System;
using System.Linq;
using System.Web.Security;
using HelloWorld.Code.DataAccess; namespace HelloWorld.Code.Security
{
public class CustomMembershipProvider : MembershipProvider
{
#region Overrides of MembershipProvider /// <summary>
/// Verifies that the specified user name and password exist in the data source.
/// </summary>
/// <returns>
/// true if the specified username and password are valid; otherwise, false.
/// </returns>
/// <param name="username">The name of the user to validate. </param><param name="password">The password for the specified user. </param>
public override bool ValidateUser(string username, string password)
{
if (string.IsNullOrEmpty(username) || string.IsNullOrEmpty(password))
return false; using (var context = new MvcDemoEntities())
{
var user = (from u in context.Users
where String.Compare(u.Username, username, StringComparison.OrdinalIgnoreCase) == 0
&& String.Compare(u.Password, password, StringComparison.OrdinalIgnoreCase) == 0
&& !u.Deleted
select u).FirstOrDefault(); return user != null;
}
} #endregion #region Overrides of MembershipProvider that throw NotImplementedException public override MembershipUser CreateUser(string username, string password, string email, string passwordQuestion, string passwordAnswer, bool isApproved, object providerUserKey, out MembershipCreateStatus status)
{
throw new NotImplementedException();
} public override bool ChangePasswordQuestionAndAnswer(string username, string password, string newPasswordQuestion, string newPasswordAnswer)
{
throw new NotImplementedException();
} public override string GetPassword(string username, string answer)
{
throw new NotImplementedException();
} public override bool ChangePassword(string username, string oldPassword, string newPassword)
{
throw new NotImplementedException();
} public override string ResetPassword(string username, string answer)
{
throw new NotImplementedException();
} public override void UpdateUser(MembershipUser user)
{
throw new NotImplementedException();
} public override bool UnlockUser(string userName)
{
throw new NotImplementedException();
} public override string GetUserNameByEmail(string email)
{
throw new NotImplementedException();
} public override bool DeleteUser(string username, bool deleteAllRelatedData)
{
throw new NotImplementedException();
} public override MembershipUserCollection GetAllUsers(int pageIndex, int pageSize, out int totalRecords)
{
throw new NotImplementedException();
} public override int GetNumberOfUsersOnline()
{
throw new NotImplementedException();
} public override MembershipUserCollection FindUsersByName(string usernameToMatch, int pageIndex, int pageSize, out int totalRecords)
{
throw new NotImplementedException();
} public override MembershipUserCollection FindUsersByEmail(string emailToMatch, int pageIndex, int pageSize, out int totalRecords)
{
throw new NotImplementedException();
} public override bool EnablePasswordRetrieval
{
get { throw new NotImplementedException(); }
} public override bool EnablePasswordReset
{
get { throw new NotImplementedException(); }
} public override bool RequiresQuestionAndAnswer
{
get { throw new NotImplementedException(); }
} public override string ApplicationName
{
get { throw new NotImplementedException(); }
set { throw new NotImplementedException(); }
} public override int MaxInvalidPasswordAttempts
{
get { throw new NotImplementedException(); }
} public override int PasswordAttemptWindow
{
get { throw new NotImplementedException(); }
} public override bool RequiresUniqueEmail
{
get { throw new NotImplementedException(); }
} public override MembershipPasswordFormat PasswordFormat
{
get { throw new NotImplementedException(); }
} public override int MinRequiredPasswordLength
{
get { throw new NotImplementedException(); }
} public override int MinRequiredNonAlphanumericCharacters
{
get { throw new NotImplementedException(); }
} public override string PasswordStrengthRegularExpression
{
get { throw new NotImplementedException(); }
} public override MembershipUser GetUser(object providerUserKey, bool userIsOnline)
{
throw new NotImplementedException();
} public override MembershipUser GetUser(string username, bool userIsOnline)
{
throw new NotImplementedException();
} #endregion
}
}

Note that we provided an implementation only for the ValidateUser method because this is the only method needed to validate the user credentials and, for the moment, we don’t need the other features of the membership provider, like, for example, the ResetPassword method.

Next we will edit the web.config file and enable forms authentication. We will also specify that we will use the CustomMembershipProvider that we just created:

<authentication mode="Forms">
<forms loginUrl="~/Account/Login" defaultUrl="~/" timeout="20" slidingExpiration="true" />
</authentication>
<membership defaultProvider="CustomMembershipProvider">
<providers>
<clear />
<add name="CustomMembershipProvider"
type="HelloWorld.Code.Security.CustomMembershipProvider" />
</providers>
</membership>

Now we will create a simple log in page that will use the Membership class to check if the user credentials are valid and the FormsAuthentication class to manage the forms authentication inside our site. The implementation is straight forward and I think the code speaks for itself so I’ll copy-paste here the code with minimal annotations.

For displaying the log in page data I created a simple view model that helps with the required data validation:

using System.ComponentModel.DataAnnotations;

namespace HelloWorld.Models
{
public class Login
{
[Required]
[Display(Name = "User name")]
public string UserName { get; set; } [Required]
[DataType(DataType.Password)]
[Display(Name = "Password")]
public string Password { get; set; } [Display(Name = "Remember me?")]
public bool RememberMe { get; set; }
}
}

This view model is used inside the view:

@model HelloWorld.Models.Login
@{
ViewBag.Title = "Log in";
} <h2>@ViewBag.Title</h2>
<hr/>
@Html.ValidationSummary(true)
<br/>
@using (Html.BeginForm(null, null, new { ReturnUrl = ViewBag.ReturnUrl }, FormMethod.Post))
{
@Html.LabelFor(m => m.UserName)
@Html.TextBoxFor(m => m.UserName)
@Html.ValidationMessageFor(m => m.UserName)
<br/>
@Html.LabelFor(m => m.Password)
@Html.PasswordFor(m => m.Password)
@Html.ValidationMessageFor(m => m.Password)
<br/>
<label>
@Html.CheckBoxFor(m => m.RememberMe)
@Html.LabelFor(m => m.RememberMe)
</label>
<br/>
<input type="submit" value="Log in" />
}

The controller class that works with this view and authenticates the users is:

using System.Web.Mvc;
using System.Web.Security;
using HelloWorld.Models; namespace HelloWorld.Controllers
{
public class AccountController : Controller
{
[HttpGet]
[AllowAnonymous]
public ActionResult Login(string returnUrl = "")
{
if (User.Identity.IsAuthenticated)
{
return LogOut();
} ViewBag.ReturnUrl = returnUrl;
return View();
} [HttpPost]
[AllowAnonymous]
public ActionResult Login(Login model, string returnUrl = "")
{
if(ModelState.IsValid)
{
if (Membership.ValidateUser(model.UserName, model.Password))
{
FormsAuthentication.RedirectFromLoginPage(model.UserName, model.RememberMe);
} ModelState.AddModelError("", "Incorrect username and/or password");
} return View(model);
} [HttpPost]
[AllowAnonymous]
public ActionResult LogOut()
{
FormsAuthentication.SignOut();
return RedirectToAction("Login", "Account", null);
}
}
}

Notice here the use of the AllowAnonymousAttribute. This attribute can be used to indicate that an action method inside a controller or a whole controller doesn’t require user authorization, hence can be accessed by anonymous users. This attribute is used in conjunction with the AuthorizeAttribute that provides a way to restrict the access. Both of this attributes can be set at action method level or at controller level, if we want the same attribute to be applied to all the action methods inside a controller.

Next, we will apply the authorize attribute to our UsersController and HomeController like this:

[Authorize]
public class HomeController : Controller
{
//code omitted for brevity
}

If we run now the application we will not be able to see the home page until we log in.

3. Implement a custom role provider 

Now we would like to allow the access to the users controller only for administrators. For doing this we will use the same AuthorizeAttribute that we used before but, this time, we will provide also the name of the user role that is needed to invoke this controller:

[Authorize(Roles = "Administrator")]
public class UsersController : Controller
{
//code omitted for brevity
}

For this to work we need to add a custom role provider that will be used to return the roles that a user has.

using System;
using System.Collections.Specialized;
using System.Linq;
using System.Data.Entity;
using System.Web;
using System.Web.Caching;
using System.Web.Security;
using HelloWorld.Code.DataAccess; namespace HelloWorld.Code.Security
{
public class CustomRoleProvider : RoleProvider
{
#region Properties private int _cacheTimeoutInMinutes = 30; #endregion #region Overrides of RoleProvider /// <summary>
/// Initialize values from web.config.
/// </summary>
/// <param name="name">The friendly name of the provider.</param>
/// <param name="config">A collection of the name/value pairs representing the provider-specific attributes specified in the configuration for this provider.</param>
public override void Initialize(string name, NameValueCollection config)
{
// Set Properties
int val;
if (!string.IsNullOrEmpty(config["cacheTimeoutInMinutes"]) && Int32.TryParse(config["cacheTimeoutInMinutes"], out val))
_cacheTimeoutInMinutes = val; // Call base method
base.Initialize(name, config);
} /// <summary>
/// Gets a value indicating whether the specified user is in the specified role for the configured applicationName.
/// </summary>
/// <returns>
/// true if the specified user is in the specified role for the configured applicationName; otherwise, false.
/// </returns>
/// <param name="username">The user name to search for.</param><param name="roleName">The role to search in.</param>
public override bool IsUserInRole(string username, string roleName)
{
var userRoles = GetRolesForUser(username);
return userRoles.Contains(roleName);
} /// <summary>
/// Gets a list of the roles that a specified user is in for the configured applicationName.
/// </summary>
/// <returns>
/// A string array containing the names of all the roles that the specified user is in for the configured applicationName.
/// </returns>
/// <param name="username">The user to return a list of roles for.</param>
public override string[] GetRolesForUser(string username)
{
//Return if the user is not authenticated
if (!HttpContext.Current.User.Identity.IsAuthenticated)
return null; //Return if present in Cache
var cacheKey = string.Format("UserRoles_{0}", username);
if (HttpRuntime.Cache[cacheKey] != null)
return (string[])HttpRuntime.Cache[cacheKey]; //Get the roles from DB
var userRoles = new string[] { };
using (var context = new MvcDemoEntities())
{
var user = (from u in context.Users.Include(usr => usr.UserRole)
where String.Compare(u.Username, username, StringComparison.OrdinalIgnoreCase) == 0
select u).FirstOrDefault(); if (user != null)
userRoles = new[]{user.UserRole.UserRoleName};
} //Store in cache
HttpRuntime.Cache.Insert(cacheKey, userRoles, null, DateTime.Now.AddMinutes(_cacheTimeoutInMinutes), Cache.NoSlidingExpiration); // Return
return userRoles.ToArray();
} #endregion #region Overrides of RoleProvider that throw NotImplementedException public override void CreateRole(string roleName)
{
throw new NotImplementedException();
} public override bool DeleteRole(string roleName, bool throwOnPopulatedRole)
{
throw new NotImplementedException();
} public override bool RoleExists(string roleName)
{
throw new NotImplementedException();
} public override void AddUsersToRoles(string[] usernames, string[] roleNames)
{
throw new NotImplementedException();
} public override void RemoveUsersFromRoles(string[] usernames, string[] roleNames)
{
throw new NotImplementedException();
} public override string[] GetUsersInRole(string roleName)
{
throw new NotImplementedException();
} public override string[] GetAllRoles()
{
throw new NotImplementedException();
} public override string[] FindUsersInRole(string roleName, string usernameToMatch)
{
throw new NotImplementedException();
} public override string ApplicationName
{
get { throw new NotImplementedException(); }
set { throw new NotImplementedException(); }
} #endregion
}
}

And, of course, we need to specify that we are using this role provider inside the web.config class:

<roleManager defaultProvider="CustomRoleProvider" enabled="true">
<providers>
<clear />
<add name="CustomRoleProvider"
type="HelloWorld.Code.Security.CustomRoleProvider"
cacheTimeoutInMinutes="30" />
</providers>
</roleManager>

Now, if we try to access the user list page while being authenticated as users we will be automatically redirected to the log in page.

Next we will add a log out link in the _Layout.chtml file, inside the header div, that will be visible only if the current user is logged in:

<span >@if (HttpContext.Current.User.Identity.IsAuthenticated)
{
@Html.ActionLink("Log out", "Logout", "Account");
}</span>

We are using HttpContext.Current to get all the HTTP-specific information for the current HTTP request. One of these properties is the User property that encapsulates the security information for the current request. The security information related to the current user is accessed through the IPrincipal interface. This interface can be used to check if the current user belong to a given role and gives access to the user’s identity through the IIdentity interface. The IIdentity encapsulates the user data and can be seen as the interface that defines who the user is while the IPrincipal object defines who the current user is and what he is allowed to do.

Sometimes it is useful to extend the default identity and principal objects to include additional user information. This is what we will do in the next section.

4. Implement a custom user principal and identity 

First we will define a custom identity class that inherits from IIdentity and receives as an input parameter the default identity object that is created by the forms authentication.

using System;
using System.Security.Principal;
using System.Web.Security; namespace HelloWorld.Code.Security
{
/// <summary>
/// An identity object represents the user on whose behalf the code is running.
/// </summary>
[Serializable]
public class CustomIdentity : IIdentity
{
#region Properties public IIdentity Identity { get; set; } public string FirstName { get; set; } public string LastName { get; set; } public string Email { get; set; } public int UserRoleId { get; set; } public string UserRoleName { get; set; } #endregion #region Implementation of IIdentity /// <summary>
/// Gets the name of the current user.
/// </summary>
/// <returns>
/// The name of the user on whose behalf the code is running.
/// </returns>
public string Name
{
get { return Identity.Name; }
} /// <summary>
/// Gets the type of authentication used.
/// </summary>
/// <returns>
/// The type of authentication used to identify the user.
/// </returns>
public string AuthenticationType
{
get { return Identity.AuthenticationType; }
} /// <summary>
/// Gets a value that indicates whether the user has been authenticated.
/// </summary>
/// <returns>
/// true if the user was authenticated; otherwise, false.
/// </returns>
public bool IsAuthenticated { get { return Identity.IsAuthenticated; } } #endregion #region Constructor public CustomIdentity(IIdentity identity)
{
Identity = identity; var customMembershipUser = (CustomMembershipUser) Membership.GetUser(identity.Name);
if(customMembershipUser != null)
{
FirstName = customMembershipUser.FirstName;
LastName = customMembershipUser.LastName;
Email = customMembershipUser.Email;
UserRoleId = customMembershipUser.UserRoleId;
UserRoleName = customMembershipUser.UserRoleName;
}
} #endregion
}
}

The constructor uses the name of the default identity and the current membership provider to get the user data. We will modify our custom membership provider by implementing the methods GetUser and Initialize. We need to implement the Initialize method because we will use caching to remember the user information between post backs and we want to be able to set the caching time in the web.config like this:

<membership defaultProvider="CustomMembershipProvider">
<providers>
<clear />
<add name="CustomMembershipProvider"
type="HelloWorld.Code.Security.CustomMembershipProvider"
cacheTimeoutInMinutes="30" />
</providers>
</membership>

In the initialize method we will read the cacheTimeoutInMinutes value as it is defined in the web.config.

private int _cacheTimeoutInMinutes = 30;

/// <summary>
/// Initialize values from web.config.
/// </summary>
/// <param name="name">The friendly name of the provider.</param>
/// <param name="config">A collection of the name/value pairs representing the provider-specific attributes specified in the configuration for this provider.</param>
public override void Initialize(string name, NameValueCollection config)
{
// Set Properties
int val;
if (!string.IsNullOrEmpty(config["cacheTimeoutInMinutes"]) && Int32.TryParse(config["cacheTimeoutInMinutes"], out val))
_cacheTimeoutInMinutes = val; // Call base method
base.Initialize(name, config);
}

Than we implement the method for retrieving the user data. Because we want to return more user information than we can store in the default MembershipUser, that is the default return type of the GetUser method, we will first create a custom implementation for the MembershipUser.

using System;
using System.Web.Security;
using HelloWorld.Code.DataAccess; namespace HelloWorld.Code.Security
{
public class CustomMembershipUser : MembershipUser
{
#region Properties public string FirstName { get; set; } public string LastName { get; set; } public int UserRoleId { get; set; } public string UserRoleName { get; set; } #endregion public CustomMembershipUser(User user)
: base("CustomMembershipProvider", user.Username, user.UserId, user.Email, string.Empty, string.Empty, true, false, DateTime.Now, DateTime.Now, DateTime.Now, DateTime.Now, DateTime.Now)
{
FirstName = user.FirstName;
LastName = user.LastName;
UserRoleId = user.UserRoleId;
UserRoleName = user.UserRole.UserRoleName;
}
}
}

Now we can implement the method for retrieving the user data inside our custom membership provider:

/// <summary>
/// Gets information from the data source for a user. Provides an option to update the last-activity date/time stamp for the user.
/// </summary>
/// <returns>
/// A <see cref="T:System.Web.Security.MembershipUser"/> object populated with the specified user's information from the data source.
/// </returns>
/// <param name="username">The name of the user to get information for. </param><param name="userIsOnline">true to update the last-activity date/time stamp for the user; false to return user information without updating the last-activity date/time stamp for the user. </param>
public override MembershipUser GetUser(string username, bool userIsOnline)
{
var cacheKey = string.Format("UserData_{0}", username);
if (HttpRuntime.Cache[cacheKey] != null)
return (CustomMembershipUser)HttpRuntime.Cache[cacheKey]; using (var context = new MvcDemoEntities())
{
var user = (from u in context.Users.Include(usr => usr.UserRole)
where String.Compare(u.Username, username, StringComparison.OrdinalIgnoreCase) == 0
&& !u.Deleted
select u).FirstOrDefault(); if (user == null)
return null; var membershipUser = new CustomMembershipUser(user); //Store in cache
HttpRuntime.Cache.Insert(cacheKey, membershipUser, null, DateTime.Now.AddMinutes(_cacheTimeoutInMinutes), Cache.NoSlidingExpiration); return membershipUser;
}
}

We will also create a custom principal that works with our new CustomIdentity class.

using System;
using System.Security.Principal; namespace HelloWorld.Code.Security
{
[Serializable]
public class CustomPrincipal : IPrincipal
{
#region Implementation of IPrincipal /// <summary>
/// Determines whether the current principal belongs to the specified role.
/// </summary>
/// <returns>
/// true if the current principal is a member of the specified role; otherwise, false.
/// </returns>
/// <param name="role">The name of the role for which to check membership. </param>
public bool IsInRole(string role)
{
return Identity is CustomIdentity &&
string.Compare(role, ((CustomIdentity) Identity).UserRoleName, StringComparison.CurrentCultureIgnoreCase) == 0;
} /// <summary>
/// Gets the identity of the current principal.
/// </summary>
/// <returns>
/// The <see cref="T:System.Security.Principal.IIdentity"/> object associated with the current principal.
/// </returns>
public IIdentity Identity { get; private set; } #endregion public CustomIdentity CustomIdentity { get { return (CustomIdentity)Identity; } set { Identity = value; } } public CustomPrincipal(CustomIdentity identity)
{
Identity = identity;
}
}
}

The only thing left is to replace the default HttpContext.Current.User with our new CustomProvider. We will do this by adding the methodApplication_PostAuthenticateRequest inside Global.asax:

protected void Application_PostAuthenticateRequest(object sender, EventArgs e)
{
if (Request.IsAuthenticated)
{
var identity = new CustomIdentity(HttpContext.Current.User.Identity);
var principal = new CustomPrincipal(identity);
HttpContext.Current.User = principal;
}
}

Now we are able to use our new custom identity and principal. For example we will modify the _Layout.chtml file to display more information about the currently logged user in the header.

<div id="header">
<i>=(^.^)= @ViewBag.Title</i>
<span >@if (HttpContext.Current.User.Identity.IsAuthenticated)
{
var identity = ((CustomPrincipal)HttpContext.Current.User).CustomIdentity;
@Html.Label(string.Format("Welcome {0} {1}, you are logged as {2}", identity.FirstName, identity.LastName, identity.UserRoleName))
@Html.ActionLink("Log out", "Logout", "Account")
}</span>
</div>

And, the final touch, we create an extension method that converts an IPrincipal object to a CustomIdentity object. We do this just because the code will be more elegant:

using System.Security.Principal;

namespace HelloWorld.Code.Security
{
public static class SecurityExtentions
{
public static CustomPrincipal ToCustomPrincipal(this IPrincipal principal)
{
return (CustomPrincipal) principal;
}
}
}

Now we can get the custom identity like this:

var identity = HttpContext.Current.User.ToCustomPrincipal().CustomIdentity;

The final result will be like this:

5. Implement a custom authorization filter 

It is possible to create custom attributes that, when applied to a controller class and/or a controller action method will perform an additional logic before or after the action method is executed. These attributes must inherit from the FilterAttribute class and must implement at least one of the following interfaces:

  1. IAuthorizationFilter
  2. IActionFilter
  3. IResultFilter
  4. IExceptionFilter

If an action method has more than one filter then they will be executed in the same order as in the list above, starting with the authorization filters and continuing with the action, result and, in the end, exception filters. You can find more information about action filtering and about the order in which the action filters are executed on MSDN.

As a first example, we will create a custom attribute that inherits from the System.Web.Mvc.AuthorizeAttribute and will allow us to pass the allowed user roles as a list of UserRole enumerations instead of passing a string containing the comma separated list of allowed user roles.

using System.Linq;

namespace HelloWorld.Code.Security
{
public class UserRoleAuthorizeAttribute : System.Web.Mvc.AuthorizeAttribute
{
public UserRoleAuthorizeAttribute(){} public UserRoleAuthorizeAttribute(params UserRole[] roles)
{
Roles = string.Join(",", roles.Select(r => r.ToString()));
}
} public enum UserRole
{
Administrator = 1,
User = 2
}
}

Usage example:

[UserRoleAuthorize(UserRole.Administrator, UserRole.User)]

As a second example we will create a new custom filter that will allow the execution of an action method only between two given hours. For example, we want to allow the user add, edit and delete functionality only between 9 in the morning and 19 in the evening. If we are outside this time frame then we will redirect the user to the home page. Again we will be inheriting the System.Web.Mvc.AuthorizeAttribute because it is easier to start from it than from the basic FilterAttributeclass.

using System;
using System.Web;
using System.Web.Mvc;
using System.Web.Routing; namespace HelloWorld.Code.Security
{
public class TimeAuthorizeAttribute : AuthorizeAttribute
{
public int StartTime { get; set; }
public int EndTime { get; set; } protected override bool AuthorizeCore(HttpContextBase httpContext)
{
if (DateTime.Now.Hour < StartTime)
return false; if (EndTime <= DateTime.Now.Hour)
return false; return true;
} protected override void HandleUnauthorizedRequest(AuthorizationContext filterContext)
{
filterContext.Result = new RedirectToRouteResult(new RouteValueDictionary { { "controller", "Home" }, { "action", "Index" } });
}
}
}

All we did was to override the method that decides if the current user is authorized AuthorizeCore. In case the user is not authorized we will redirect him to the home page. We accomplish this by overriding the method HandleUnauthorizedRequest.

6. Summary 

In this post we had a look on how to implement custom authentication in an ASP.NET MVC 4 application. You can download the source code that contains all the points discussed here from Codeplex.

Forms Authentication in ASP.NET MVC 4的更多相关文章

  1. [转]Implementing User Authentication in ASP.NET MVC 6

    本文转自:http://www.dotnetcurry.com/aspnet-mvc/1229/user-authentication-aspnet-mvc-6-identity In this ar ...

  2. Active Directory Authentication in ASP.NET MVC 5 with Forms Authentication and Group-Based Authorization

    I know that blog post title is sure a mouth-full, but it describes the whole problem I was trying to ...

  3. 【MVC】ASP.NET MVC Forms验证机制

    http://www.cnblogs.com/bomo/p/3309766.html 随笔 - 121  文章 - 0  评论 - 92 [MVC]ASP.NET MVC Forms验证机制 ASP. ...

  4. ASP.NET MVC:Form Authentication 相关的学习资源

    看完此图就懂了 看完下面文章必须精通 Form authentication and authorization in ASP.NET Explained: Forms Authentication ...

  5. [译]Asp.net MVC 之 Contorllers(一)

    Asp.net MVC contorllers 在Ajax全面开花的时代,ASP.NET Web Forms 开始慢慢变得落后.有人说,Ajax已经给了Asp.net致命一击.Ajax使越来越多的控制 ...

  6. Incorporating ASP.NET MVC and SQL Server Reporting Services, Part 1

    Your ASP.NET MVC application needs reports. What do you do? In this article, I will demonstrate how ...

  7. asp.net mvc vs web form

    译者介绍 小小.NET学童,滴答…滴答…的雨…… 正文如下======================================================= 原文示例(VS2012): 1 ...

  8. 推荐几本不错的ASP.NET MVC书

    以前主要是做PHP应用的,由于工作需要,捡起来.NET, 特别是新技术层出不穷,找了几本书看,个人感觉还不错,网上也有电子版的下载 一. ASP.NET MVC4 Web 编程 O'Reilly出版社 ...

  9. Forms Authentication and Role based Authorization: A Quicker, Simpler, and Correct Approach

    https://www.codeproject.com/Articles/36836/Forms-Authentication-and-Role-based-Authorization Problem ...

随机推荐

  1. RQNOJ PID3 / Jam的计数法

    题目描述 Jam是个喜欢标新立异的科学怪人.他不使用阿拉伯数字计数,而是使用小写英文字母计数,他觉得这样做,会使世界更加丰富多彩.在他的计数法中,每个数字的位数都是相同的(使用相同个数的字母),英文字 ...

  2. View Component

    View Component 在之前的MVC中,我们经常需要类似一种小部件的功能,通常我们都是使用Partial View来实现,因为MVC中没有类似Web Forms中的WebControl的功能. ...

  3. 后台自动启动appium

    首先说明,本人用的exe方式安装的appium. 新建一个.vbs文件,写入以下脚本,记得把D盘换成你自己的盘符. 1.后面taskkill好像没有实际作用..加就加了吧. Set ws = Crea ...

  4. [CLR via C#]7. 常量和字段

    原文:[CLR via C#]7. 常量和字段 7.1 常量 常量(constant)是一个特殊的值,它是一个从不变化的值. 在定义常量时,它的值必须在编译时确定.确定之后,编译器将常量的值保存到程序 ...

  5. POJ 3176-Cow Bowling(DP||记忆化搜索)

    Cow Bowling Time Limit: 1000MS   Memory Limit: 65536K Total Submissions: 14210   Accepted: 9432 Desc ...

  6. ArcGIS API for Silverlight 编辑Geometry

    概述 ArcMap的编辑功能是非常强大的,ArcEngine编写的CS程序也可以用到ArcMap中提供的编辑功能,那么ArcGIS API forSilverlight针对Geometry的编辑提供了 ...

  7. 网站静态化处理—CSI(5)

    网站静态化处理—CSI(5) 讲完了SSI,ESI,下面就要讲讲CSI了 ,CSI是浏览器端的动静整合方案,当我文章发表后有朋友就问我,CSI技术是不是就是通过ajax来加载数据啊,我当时的回答只是说 ...

  8. Ubuntu 14.04 关机键无效解决方法

         这几天開始研究ubuntu 14.04软件,安装Cairo-Dock后发现右上角的关机.重新启动.注销菜单点击都没了反应仅仅能通过命令实现,后来经过研究,发现仅仅要设置了 Cairo-Doc ...

  9. js前端分页之jQuery

    锋利的js前端分页之jQuery 大家在作分页时,多数是在后台返回一个导航条的html字符串,其实在前端用js也很好实现. 调用pager方法,输入参数,会返回一个导航条的html字符串.方法的内部比 ...

  10. C# ASP.NET CSV文件导入数据库

    原文:C# ASP.NET CSV文件导入数据库 using System; using System.Collections.Generic; using System.Text; using Sy ...