原文: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. cer证书签名验证

    一个cer还需要一个签名的证书本身,这是为了防止cer证书被篡改. 有两种类型的证书: 1. 根证书 2. 由根证书颁发子证书. 特根证书.它是自签名. 而其它子证书的签名公钥都保存在它的上级证书里面 ...

  2. 蜗牛—JSP学习乘法表页面输出

    <%@page import="java.text.SimpleDateFormat" pageEncoding="utf-8"%> <%@ ...

  3. linux_vim_快捷键

    1.vim ~/.vimrc 进入配置文件 如果不知道vimrc文件在哪,可使用 :scriptnames 来查看 set nu #行号 set tabstop=4 #一个tab为4个空格长度 set ...

  4. B二分法

    <span style="color:#330099;">/* B - 二分法 基金会 Time Limit:1000MS Memory Limit:65536KB 6 ...

  5. 基于webrtc技术session border controler (SBC)

    由于原来的文章 http://blog.csdn.net/voipmaker  转载注明出处. 我建了一个通信学习 交流群. 45211986, 欢迎增加. WebRTC技术致力于在浏览器端实现实时音 ...

  6. Linux经常使用命令(一) - ls

    ls命令是linux下最经常使用的命令.ls命令就是list的缩写, 缺省下ls用来打印出当前文件夹的清单, 假设ls指定其它文件夹, 那么就会显示指定文件夹里的文件及文件夹清单. 通过ls 命令不仅 ...

  7. ASP.NET MVC中对Model进行分步验证的解决方法

    原文:ASP.NET MVC中对Model进行分步验证的解决方法 在我之前的文章:ASP.NET MVC2.0结合WF4.0实现用户多步注册流程中将一个用户的注册分成了四步,而这四个步骤都是在完善一个 ...

  8. svg的自述

    svg可缩放矢量图形(Scalable Vector Graphics). SVG 使用 XML 格式定义图像. SVG 是使用 XML 来描述二维图形和绘图程序的语言. 什么是SVG? SVG 指可 ...

  9. AngularJS html5Mode与ASP.NET MVC路由

    AngularJS html5Mode与ASP.NET MVC路由共存 前言 很久之前便听说AngularJS,非常酷,最近也比较火,我也在持续关注这个技术,只是没有认真投入学习.前不久公司找我们部门 ...

  10. IOS被遗忘的知识

    IOS ARC项目使用非ARC文件 1.自己的旧项目没有使用ARC,可是引入的第三方库却是使用了ARC的. 对于第一个情况,给採用了ARC的源文件,加入-fobjc-arc选项 2.自己的新项目使用了 ...