Asp.net MVC身份驗證及權限管理

一、項目結構

在這裏插入圖片描述

二、編寫代碼

(1)創建實體類

AccountModels .cs

namespace WebApplication1.Models
{

    #region Models
    [PropertiesMustMatch("NewPassword", "ConfirmPassword", ErrorMessage = "The new password and confirmation password do not match.")]
    public class ChangePasswordModel
    {
        [Required]
        [DataType(DataType.Password)]
        [DisplayName("Current password")]
        public string OldPassword { get; set; }

        [Required]
        [ValidatePasswordLength]
        [DataType(DataType.Password)]
        [DisplayName("New password")]
        public string NewPassword { get; set; }

        [Required]
        [DataType(DataType.Password)]
        [DisplayName("Confirm new password")]
        public string ConfirmPassword { get; set; }
    }

    public class LogOnModel
    {
        [Required]
        [DisplayName("User name")]
        public string UserName { get; set; }

        [Required]
        [DataType(DataType.Password)]
        [DisplayName("Password")]
        public string Password { get; set; }

        [DisplayName("Remember me?")]
        public bool RememberMe { get; set; }
    }

    [PropertiesMustMatch("Password", "ConfirmPassword", ErrorMessage = "The password and confirmation password do not match.")]
    public class RegisterModel
    {
        [Required]
        [DisplayName("User name")]
        public string UserName { get; set; }

        [Required]
        [DataType(DataType.EmailAddress)]
        [DisplayName("Email address")]
        public string Email { get; set; }

        [Required]
        [ValidatePasswordLength]
        [DataType(DataType.Password)]
        [DisplayName("Password")]
        public string Password { get; set; }

        [Required]
        [DataType(DataType.Password)]
        [DisplayName("Confirm password")]
        public string ConfirmPassword { get; set; }
    }
    #endregion

    #region Services
    // The FormsAuthentication type is sealed and contains static members, so it is difficult to
    // unit test code that calls its members. The interface and helper class below demonstrate
    // how to create an abstract wrapper around such a type in order to make the AccountController
    // code unit testable.

    public interface IMembershipService
    {
        int MinPasswordLength { get; }

        bool ValidateUser(string userName, string password);
        MembershipCreateStatus CreateUser(string userName, string password, string email);
        bool ChangePassword(string userName, string oldPassword, string newPassword);
    }

    public class AccountMembershipService : IMembershipService
    {
        private readonly MembershipProvider _provider;

        public AccountMembershipService()
            : this(null)
        {
        }

        public AccountMembershipService(MembershipProvider provider)
        {
            _provider = provider ?? Membership.Provider;
        }

        public int MinPasswordLength
        {
            get
            {
                return _provider.MinRequiredPasswordLength;
            }
        }

        public bool ValidateUser(string userName, string password)
        {
            if (String.IsNullOrEmpty(userName)) throw new ArgumentException("Value cannot be null or empty.", "userName");
            if (String.IsNullOrEmpty(password)) throw new ArgumentException("Value cannot be null or empty.", "password");

            return _provider.ValidateUser(userName, password);
        }

        public MembershipCreateStatus CreateUser(string userName, string password, string email)
        {
            if (String.IsNullOrEmpty(userName)) throw new ArgumentException("Value cannot be null or empty.", "userName");
            if (String.IsNullOrEmpty(password)) throw new ArgumentException("Value cannot be null or empty.", "password");
            if (String.IsNullOrEmpty(email)) throw new ArgumentException("Value cannot be null or empty.", "email");

            MembershipCreateStatus status;
            _provider.CreateUser(userName, password, email, null, null, true, null, out status);
            return status;
        }

        public bool ChangePassword(string userName, string oldPassword, string newPassword)
        {
            if (String.IsNullOrEmpty(userName)) throw new ArgumentException("Value cannot be null or empty.", "userName");
            if (String.IsNullOrEmpty(oldPassword)) throw new ArgumentException("Value cannot be null or empty.", "oldPassword");
            if (String.IsNullOrEmpty(newPassword)) throw new ArgumentException("Value cannot be null or empty.", "newPassword");

            // The underlying ChangePassword() will throw an exception rather
            // than return false in certain failure scenarios.
            try
            {
                MembershipUser currentUser = _provider.GetUser(userName, true /* userIsOnline */);
                return currentUser.ChangePassword(oldPassword, newPassword);
            }
            catch (ArgumentException)
            {
                return false;
            }
            catch (MembershipPasswordException)
            {
                return false;
            }
        }
    }

    public interface IFormsAuthenticationService
    {
        void SignIn(string userName, bool createPersistentCookie);
        void SignOut();
    }

    public class FormsAuthenticationService : IFormsAuthenticationService
    {
        public void SignIn(string userName, bool createPersistentCookie)
        {
            if (String.IsNullOrEmpty(userName)) throw new ArgumentException("Value cannot be null or empty.", "userName");

            FormsAuthentication.SetAuthCookie(userName, createPersistentCookie);
        }

        public void SignOut()
        {
            FormsAuthentication.SignOut();
        }
    }
    #endregion

    #region Validation
    public static class AccountValidation
    {
        public static string ErrorCodeToString(MembershipCreateStatus createStatus)
        {
            // See http://go.microsoft.com/fwlink/?LinkID=177550 for
            // a full list of status codes.
            switch (createStatus)
            {
                case MembershipCreateStatus.DuplicateUserName:
                    return "Username already exists. Please enter a different user name.";

                case MembershipCreateStatus.DuplicateEmail:
                    return "A username for that e-mail address already exists. Please enter a different e-mail address.";

                case MembershipCreateStatus.InvalidPassword:
                    return "The password provided is invalid. Please enter a valid password value.";

                case MembershipCreateStatus.InvalidEmail:
                    return "The e-mail address provided is invalid. Please check the value and try again.";

                case MembershipCreateStatus.InvalidAnswer:
                    return "The password retrieval answer provided is invalid. Please check the value and try again.";

                case MembershipCreateStatus.InvalidQuestion:
                    return "The password retrieval question provided is invalid. Please check the value and try again.";

                case MembershipCreateStatus.InvalidUserName:
                    return "The user name provided is invalid. Please check the value and try again.";

                case MembershipCreateStatus.ProviderError:
                    return "The authentication provider returned an error. Please verify your entry and try again. If the problem persists, please contact your system administrator.";

                case MembershipCreateStatus.UserRejected:
                    return "The user creation request has been canceled. Please verify your entry and try again. If the problem persists, please contact your system administrator.";

                default:
                    return "An unknown error occurred. Please verify your entry and try again. If the problem persists, please contact your system administrator.";
            }
        }
    }

    [AttributeUsage(AttributeTargets.Class, AllowMultiple = true, Inherited = true)]
    public sealed class PropertiesMustMatchAttribute : ValidationAttribute
    {
        private const string _defaultErrorMessage = "'{0}' and '{1}' do not match.";
        private readonly object _typeId = new object();

        public PropertiesMustMatchAttribute(string originalProperty, string confirmProperty)
            : base(_defaultErrorMessage)
        {
            OriginalProperty = originalProperty;
            ConfirmProperty = confirmProperty;
        }

        public string ConfirmProperty { get; private set; }
        public string OriginalProperty { get; private set; }

        public override object TypeId
        {
            get
            {
                return _typeId;
            }
        }

        public override string FormatErrorMessage(string name)
        {
            return String.Format(CultureInfo.CurrentUICulture, ErrorMessageString,
                OriginalProperty, ConfirmProperty);
        }

        public override bool IsValid(object value)
        {
            PropertyDescriptorCollection properties = TypeDescriptor.GetProperties(value);
            object originalValue = properties.Find(OriginalProperty, true /* ignoreCase */).GetValue(value);
            object confirmValue = properties.Find(ConfirmProperty, true /* ignoreCase */).GetValue(value);
            return Object.Equals(originalValue, confirmValue);
        }
    }

    [AttributeUsage(AttributeTargets.Field | AttributeTargets.Property, AllowMultiple = false, Inherited = true)]
    public sealed class ValidatePasswordLengthAttribute : ValidationAttribute
    {
        private const string _defaultErrorMessage = "'{0}' must be at least {1} characters long.";
        private readonly int _minCharacters = Membership.Provider.MinRequiredPasswordLength;

        public ValidatePasswordLengthAttribute()
            : base(_defaultErrorMessage)
        {
        }

        public override string FormatErrorMessage(string name)
        {
            return String.Format(CultureInfo.CurrentUICulture, ErrorMessageString,
                name, _minCharacters);
        }

        public override bool IsValid(object value)
        {
            string valueAsString = value as string;
            return (valueAsString != null && valueAsString.Length >= _minCharacters);
        }
    }
    #endregion

}

User

namespace WebApplication1.Models
{
    public class User
    {
        public int ID { get; set; }
        public string Name { get; set; }
        public string Password { get; set; }
        public string[] Roles { get; set;  }
    }
}

(2)Repository:對數據進行操作

UserRepository.cs

沒有把數據放在數據庫中,這裏採用模擬數據的方式。

namespace WebApplication1.Repositoty
{
    public class UserRepository
    {
        private static User[] usersForTest = new[]{
        new User{ ID = 1, Name = "bob", Password = "bob", Roles = new []{"employee"}},
        new User{ ID = 2, Name = "tom", Password = "tom", Roles = new []{"manager"}},
        new User{ ID = 3, Name = "admin", Password = "admin", Roles = new[]{"admin"}},
    };

        public bool ValidateUser(string userName, string password)
        {
            return usersForTest
                .Any(u => u.Name == userName && u.Password == password);
        }

        public string[] GetRoles(string userName)
        {
            return usersForTest
                .Where(u => u.Name == userName)
                .Select(u => u.Roles)
                .FirstOrDefault();
        }

        public User GetByNameAndPassword(string name, string password)
        {
            return usersForTest
                .FirstOrDefault(u => u.Name == name && u.Password == password);
        }
    }
}

(3)配置Web.config

在web.config文件中,
<system.web>配置節用於對驗證進行配置。
爲節點提供mode="Forms"屬性
可以啓用Forms Authentication。
當前項目的配置節如下所示:

  <system.web>
    <authentication mode="Forms">
      <forms 
        loginUrl="~/Account/LogOn" 
        defaultUrl="~/Home/Index" 
        protection="All"/>
    </authentication>
   ......
   ......
  </system.web>
  • loginUrl:表示登錄頁面的連接,如果在未登錄(授權)的狀態下訪問需要權限的頁面,則跳轉到~/Account/LogOn
  • defaultUrl:登陸後默認跳轉的頁面
  • protection:表示對哪些頁面生效

(4)修改 Global.asax

namespace WebApplication1
{
    public class MvcApplication : System.Web.HttpApplication
    {
        
        protected void Application_Start()/*默認的配置*/
        {
            AreaRegistration.RegisterAllAreas();
            FilterConfig.RegisterGlobalFilters(GlobalFilters.Filters);
            RouteConfig.RegisterRoutes(RouteTable.Routes);
            BundleConfig.RegisterBundles(BundleTable.Bundles);
        }

        //此方式將用戶的角色保存至用戶 Cookie,使用到了 FormsAuthenticationTicket。
        
        /// <summary>
        /// 添加構造函數
        /// </summary>
        public MvcApplication()
        {
            AuthorizeRequest += new EventHandler(MvcApplication_AuthorizeRequest);
        }
        //權限請求
        void MvcApplication_AuthorizeRequest(object sender, EventArgs e)
        {
            var id = Context.User.Identity as FormsIdentity;
            if (id != null && id.IsAuthenticated)//這裏判斷訪問者是否成功進行了身份驗證
            {
                //通過逗號分割,多個權限
                var roles = id.Ticket.UserData.Split(',');
                //得到請求
                Context.User = new GenericPrincipal(id, roles);
            }
        }
    }
}

(5)Controller控制器

AccountController

public class AccountController : Controller
    {
        private UserRepository repository = new UserRepository();

        public ActionResult LogOn()
        {
            return View();
        }

        [HttpPost]
        public ActionResult LogOnss(LogOnModel model)
        {
            //if (ModelState.IsValid)
            //{
                //找用戶
                User user = repository.GetByNameAndPassword(model.UserName, model.Password);
                //找到
                if (user != null)
                {
                //提供對Form的身份驗證用於確定用戶身份
                    FormsAuthenticationTicket ticket = new FormsAuthenticationTicket(
                        1,          //版本
                        user.Name,  //名稱
                        DateTime.Now,//發行時間
                        DateTime.Now.Add(FormsAuthentication.Timeout),//到期時間
                        model.RememberMe,//持久化
                        user.Roles.Aggregate((i, j) => i + "," + j)//特定於用戶的數據
                        );
                    HttpCookie cookie = new HttpCookie(//創建cookie
                        FormsAuthentication.FormsCookieName,//name
                        FormsAuthentication.Encrypt(ticket));//value
                cookie.Expires = DateTime.Now.AddSeconds(10);//十秒鐘過期
                    Response.Cookies.Add(cookie);//添加cookie

                    //if (!String.IsNullOrEmpty(returnUrl)) return Redirect(returnUrl);else 
                return RedirectToAction("Index", "Home");//登錄成功後跳轉
                }
                else
                    ModelState.AddModelError("", "用戶名或密碼不正確!");
            //}

            return Content("success");
        }

        public ActionResult LogOff()
        {
            FormsAuthentication.SignOut();
            return RedirectToAction("Index", "Home");
        }
    }

HomeController

權限放在了User表Roles字段中,可以在UserRepository中修改其值。

public class HomeController : Controller
    {
        //有一種權限即可登錄employee or manager
        [Authorize(Roles = "employee,manager")]
        public ActionResult Index()
        {
            return View();
        }

        //admin權限
        [Authorize(Roles = "admin")]
        public ActionResult About()
        {
            ViewBag.Message = "Your application description page.";

            return View();
        }
        [Authorize(Roles = "employee")]
        public ActionResult Contact()
        {
            ViewBag.Message = "Your contact page.";

            return View();
        }
    }

三、Views下面的視圖

LogOn.cshtml

<div class="well well-sm" style="width:700px;margin-left:200px">
        <div class="modal-body">
            <div class="row">
                <div class="col-md-6 col-md-offset-3">
                    <h1 style="width:700px;margin-left:50px">&nbsp;&nbsp;&nbsp;</h1>

                    <form class="form-group" action="/Account/LogOnss" method="post">
                        <label>name</label>
                        <input type="text" name="UserName" class="form-control" />
                        <label>password</label>
                        <input type="password" name="Password" class="form-control" />
                        <br />
                        <input value="登          錄" class="btn btn-primary" type="submit" style="width:420px" />

                    </form>
                </div>
            </div>
        </div>
    </div>

源碼:DotNet

參考博客

MVC身份驗證及權限管理

ASP.NET MVC中的Global.asax文件

MVC身份驗證及權限管理

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章