首頁
編程語言
C語言|JAVA編程
Python編程
網頁編程
ASP編程|PHP編程
JSP編程
數據庫
MYSQL數據庫|SqlServer數據庫
Oracle數據庫|DB2數據庫
您现在的位置: 程式碼之家 >> 編程語言 >  >> .NET 網頁程式設計 >> ASP.NET 基礎 >> ASP.NET 基礎

ASP.NET MVC5 會員中心:個人資料修改與密碼變更(第6篇)

     編輯:ASP.NET 基礎

功能概覽

會員中心包含:側邊導航、顯示/修改個人資料、修改密碼、登出。核心技術:TryUpdateModel 白名單綁定、ChangePasswordViewModel 驗證、Sha256 密碼雜湊。

一、登入邏輯補完:更新最後登入資訊

[HttpPost]
[ValidateAntiForgeryToken]
public ActionResult Login(LoginViewModel model, string returnUrl)
{
    if (ModelState.IsValid)
    {
        var user = _userService.Find(model.UserName);
        if (user == null) ModelState.AddModelError("UserName", "用戶不存在");
        else if (user.Password == SecurityHelper.Sha256(model.Password))
        {
            // 更新最後登入時間與 IP
            user.LastLoginTime = DateTime.Now;
            user.LastLoginIP = Request.UserHostAddress;
            _userService.Update(user);

            // 建立身分識別並登入
            var identity = _userService.CreateIdentity(user, DefaultAuthenticationTypes.ApplicationCookie);
            AuthenticationManager.SignOut(DefaultAuthenticationTypes.ApplicationCookie);
            AuthenticationManager.SignIn(new AuthenticationProperties { IsPersistent = model.RememberMe }, identity);

            return RedirectToLocal(returnUrl);
        }
        else ModelState.AddModelError("Password", "密碼錯誤");
    }
    return View(model);
}

二、側邊導航選單

Partial View:_UserMenu.cshtml

<div class="panel panel-primary">
    <div class="panel-heading"><h3>我的資料</h3></div>
    <div class="panel-body">
        <ul class="nav nav-pills nav-stacked">
            <li><a href="@Url.Action("Details")"><span class="glyphicon glyphicon-user"></span> 修改資料</a></li>
            <li><a href="@Url.Action("ChangePassword")"><span class="glyphicon glyphicon-lock"></span> 修改密碼</a></li>
            <li><a href="@Url.Action("Logout")"><span class="glyphicon glyphicon-log-out"></span> 退出登錄</a></li>
        </ul>
    </div>
</div>

UserController - Menu Action

public ActionResult Menu() => PartialView("_UserMenu");

側邊選單效果

三、顯示與修改個人資料

顯示:Details Action

public ActionResult Details()
{
    var user = _userService.Find(User.Identity.Name);
    return View(user);
}

View:Details.cshtml

@model Ninesky.Models.User

@{
    ViewBag.Title = "我的資料";
}

<div class="row">
    <div class="col-md-3">@Html.Action("Menu")</div>
    <div class="col-md-9">
        <ol class="breadcrumb">
            <li><a href="@Url.Action("Index", "Home")"><span class="glyphicon glyphicon-home"></span> 會員中心</a></li>
            <li><a href="@Url.Action("Details")">個人中心</a></li>
            <li class="active">修改資料</li>
        </ol>

        @using (Html.BeginForm("Modify", "User"))
        {
            @Html.AntiForgeryToken()
            <div class="form-horizontal">
                <h4>用戶資料</h4> <hr />
                @Html.ValidationSummary(true)
                @Html.HiddenFor(m => m.UserID)

                <div class="form-group">
                    @Html.LabelFor(m => m.UserName, new { @class = "control-label col-md-2" })
                    <div class="col-md-10">@Html.DisplayFor(m => m.UserName)</div>
                </div>

                <div class="form-group">
                    @Html.LabelFor(m => m.DisplayName, new { @class = "control-label col-md-2" })
                    <div class="col-md-10">
                        @Html.EditorFor(m => m.DisplayName)
                        @Html.ValidationMessageFor(m => m.DisplayName)
                    </div>
                </div>

                <div class="form-group">
                    <label class="control-label col-md-2">用戶組</label>
                    <div class="col-md-10">
                        @foreach (var r in Model.UserRoleRelations)
                        { <span>@r.Role.Name</span><br /> }
                    </div>
                </div>

                <div class="form-group">
                    @Html.LabelFor(m => m.Email, new { @class = "control-label col-md-2" })
                    <div class="col-md-10">
                        @Html.EditorFor(m => m.Email)
                        @Html.ValidationMessageFor(m => m.Email)
                    </div>
                </div>

                <div class="form-group">
                    <div class="col-md-offset-2 col-md-10">
                        <input type="submit" value="修改" class="btn btn-default" />
                    </div>
                </div>
            </div>
        }
    </div>
</div>

@section Scripts { @Scripts.Render("~/bundles/jqueryval") }

個人資料頁面

修改:Modify Action(TryUpdateModel 白名單)

[HttpPost]
[ValidateAntiForgeryToken]
public ActionResult Modify()
{
    var user = _userService.Find(User.Identity.Name);
    if (user == null) ModelState.AddModelError("", "用戶不存在");
    else
    {
        // 僅允許更新 DisplayName、Email
        if (TryUpdateModel(user, new[] { "DisplayName", "Email" }))
        {
            if (ModelState.IsValid)
            {
                if (_userService.Update(user))
                    ModelState.AddModelError("", "修改成功!");
                else
                    ModelState.AddModelError("", "無變更內容");
            }
        }
        else ModelState.AddModelError("", "模型綁定失敗");
    }
    return View("Details", user);
}

四、修改密碼

ViewModel:ChangePasswordViewModel.cs

public class ChangePasswordViewModel
{
    [Required(ErrorMessage = "必填")]
    [StringLength(20, MinimumLength = 6, ErrorMessage = "{2}~{1}字元")]
    [DataType(DataType.Password)]
    [Display(Name = "原密碼")]
    public string OriginalPassword { get; set; }

    [Required(ErrorMessage = "必填")]
    [StringLength(20, MinimumLength = 6, ErrorMessage = "{2}~{1}字元")]
    [DataType(DataType.Password)]
    [Display(Name = "新密碼")]
    public string Password { get; set; }

    [Required(ErrorMessage = "必填")]
    [Compare("Password", ErrorMessage = "兩次輸入不一致")]
    [DataType(DataType.Password)]
    [Display(Name = "確認密碼")]
    public string ConfirmPassword { get; set; }
}

Controller Actions

public ActionResult ChangePassword() => View();

[HttpPost]
[ValidateAntiForgeryToken]
public ActionResult ChangePassword(ChangePasswordViewModel model)
{
    if (ModelState.IsValid)
    {
        var user = _userService.Find(User.Identity.Name);
        if (user.Password == SecurityHelper.Sha256(model.OriginalPassword))
        {
            user.Password = SecurityHelper.Sha256(model.Password);
            if (_userService.Update(user))
                ModelState.AddModelError("", "密碼修改成功");
            else
                ModelState.AddModelError("", "修改失敗");
        }
        else ModelState.AddModelError("OriginalPassword", "原密碼錯誤");
    }
    return View(model);
}

View:ChangePassword.cshtml

@model Ninesky.Web.Areas.Member.Models.ChangePasswordViewModel

@{
    ViewBag.Title = "修改密碼";
}

<div class="row">
    <div class="col-md-3">@Html.Action("Menu")</div>
    <div class="col-md-9">
        @* Breadcrumb *@
        @using (Html.BeginForm())
        {
            @Html.AntiForgeryToken()
            <div class="form-horizontal">
                <h4>修改密碼</h4> <hr />
                @Html.ValidationSummary(true)

                @* OriginalPassword, Password, ConfirmPassword 三欄位 *@

                <div class="form-group">
                    <div class="col-md-offset-2 col-md-10">
                        <input type="submit" value="修改" class="btn btn-default" />
                    </div>
                </div>
            </div>
        }
    </div>
</div>

@section Scripts { @Scripts.Render("~/bundles/jqueryval") }

五、全站登入/登出連結

Shared/_LoginPartial.cshtml

@using Microsoft.AspNet.Identity

@if (Request.IsAuthenticated)
{
    using (Html.BeginForm("Logout", "User", FormMethod.Post, new { id = "logoutForm", @class = "navbar-right" }))
    {
        @Html.AntiForgeryToken()
        <ul class="nav navbar-nav navbar-right">
            <li>@Html.ActionLink("你好 " + User.Identity.GetUserName() + "!", "Manage", "Account", null, new { title = "管理" })</li>
            <li><a href="javascript:document.getElementById('logoutForm').submit()">注銷</a></li>
        </ul>
    }
}
else
{
    <ul class="nav navbar-nav navbar-right">
        <li>@Html.ActionLink("註冊", "Register", "User", new { Area = "Member" }, new { id = "registerLink" })</li>
        <li>@Html.ActionLink("登錄", "Login", "User", new { Area = "Member" }, new { id = "loginLink" })</li>
    </ul>
}

未登入狀態

已登入狀態

六、權限控制

為 Member 區域控制器加上 [Authorize],登入/註冊/驗證碼 Action 加上 [AllowAnonymous]。

關鍵技術總結

技術應用場景關鍵代碼
TryUpdateModel 白名單防參數綁定攻擊,僅允許修改指定欄位TryUpdateModel(user, new[] { "DisplayName", "Email" })
ChangePasswordViewModel密碼變更專用驗證模型,含 Compare 屬性[Compare("Password")]
Sha256 雜湊密碼不以明文儲存SecurityHelper.Sha256(password)
_LoginPartial全站共用登入狀態顯示@if (Request.IsAuthenticated)
Area + Authorize會員區統一權限管控[Authorize] on Controller

結語

會員中心基礎功能告一段落。TryUpdateModel 白名單綁定是防止過度發佈攻擊的標準做法;ChangePasswordViewModel 封裝密碼變更驗證邏輯,配合 DataAnnotations 實現伺服器/客戶端雙重驗證。下一階段進入內容管理模組(文章、諮詢、附件)。

  1. 上一篇文章:
  2. 下一篇文章:
Copyright © 程式碼之家 All Rights Reserved