ASP.NET MVC5 網站開發實踐(二) Member區域 - 用戶部分(1)用戶注冊


上一次把基本框架搭建起來了,這次開始整Web部分,終於可以看到界面了小激動一下。web項目部分從用戶功能開始,基本有注冊,登錄、注銷、查找、查看、刪除等涉及Member區域和Manage區域。

目錄:

ASP.NET MVC5 網站開發實踐 - 概述

ASP.NET MVC5 網站開發實踐(一) - 項目框架

ASP.NET MVC5 網站開發實踐(一) - 框架(續) 模型、數據存儲、業務邏輯

 

 

一、默認Web項目的更改

在上一次中我們創建了一個使用個人用戶賬戶的mvc項目,通過分析自動生成的用戶管理代碼(見《VS2013中web項目中自動生成的ASP.NET Identity代碼思考》),我覺用戶這部分還是自己做,所以刪除自動生成的用戶相關代碼。

image_thumb

 

二、添加Member區域

在web項目上點右鍵 添加 區域Member。

image

添加Home控制器,選擇MVC5控制器-空

image

我們給public ActionResult Index()添加一個視圖,代碼很簡單就是顯示下用戶名

@{
    ViewBag.Title = "會員中心";
}

<h2>歡迎你!@User.Identity.Name 
    </h2>

我們先運行一下,出錯啦。

image

這是因為項目中有兩個名為Home的控制器,必須在路由中加上命名空間。先打開區域中的MemberAreaRegistration添加命名空間。

image

再打開項目中的RouteConfig,添加命名空間

image

再刷新瀏覽器,可以正常顯示。

再添加用戶控制器UserController。

三、模型類的更改

在這里先對Models項目User模型進行修改,原來考慮的是每個用戶只能屬於一個用戶組,后來仔細考慮了一下,還是不太合適,比如一個用戶兼任多個角色,所以還是把用戶和用戶組改成一對多的關系。

  • User模型。在模型中刪除GroupID,刪除外鍵Group。
  • Role模型。原來UserGroup(用戶組)改為角色,考慮到權限管理感覺叫角色比叫戶組更加合適,另外角色的含義更廣,可以是指用戶組,也可以指職位,還可以指部門……修改后代碼如下:
using System.ComponentModel.DataAnnotations;

namespace Ninesky.Models
{
    /// <summary>
    /// 角色
    /// <remarks>
    /// 創建:2014.02.02
    /// 修改:2014.02.16
    /// </remarks>
    /// </summary>
    public class Role
    {
        [Key]
        public int RoleID { get; set; }

        /// <summary>
        /// 名稱
        /// </summary>
        [Required(ErrorMessage="必填")]
        [StringLength(20, MinimumLength = 2, ErrorMessage = "{1}到{0}個字")]
        [Display(Name="名稱")]
        public string Name { get; set; }

        /// <summary>
        /// 角色類型<br />
        /// 0普通(普通注冊用戶),1特權(像VIP之類的類型),3管理(管理權限的類型)
        /// </summary>
        [Required(ErrorMessage = "必填")]
        [Display(Name = "用戶組類型")]
        public int Type { get; set; }

        /// <summary>
        /// 說明
        /// </summary>
        [Required(ErrorMessage = "必填")]
        [StringLength(50, ErrorMessage = "少於{0}個字")]
        [Display(Name = "說明")]
        public string Description { get; set; }

        /// <summary>
        /// 獲取角色類型名稱
        /// </summary>
        /// <returns></returns>
        public string TypeToString()
        {
            switch (Type)
            {
                case 0:
                    return "普通";
                case 1:
                    return "特權";
                case 2:
                    return "管理";
                default:
                    return "未知";
            }
        }
    }
}
  • UserRoleRelation類。在Models項目添加角色關系類UserRoleRelation類,代碼:
    using System.ComponentModel.DataAnnotations;
    
    namespace Ninesky.Models
    {
        /// <summary>
        /// 用戶角色關系
        /// <remarks>
        /// 創建:2014.02.16
        /// </remarks>
        /// </summary>
        public class UserRoleRelation
        {
            [Key]
            public int RelationID { get; set; }
    
            /// <summary>
            /// 用戶ID
            /// </summary>
            [Required()]
            public int UserID { get; set; }
            
            /// <summary>
            /// 角色ID
            /// </summary>
            [Required()]
            public int RoelID { get; set; }
        }
    }
  • NineskyDbContext類。 如下圖藍色框為修改部分,紅框為新增加

image

三、驗證碼及Sha256加密

1、驗證碼

現在驗證碼是網站的必須功能,我把驗證碼功能分成三塊:創建驗證碼字符、根據驗證碼生成圖片、User控制器action中保存驗證碼並返回圖片。

創建驗證碼字符 CreateVerificationText()

在Common中添加Security類,在類中利用偽隨機數生成器生成驗證碼字符串。

/// <summary>
        /// 創建驗證碼字符
        /// </summary>
        /// <param name="length">字符長度</param>
        /// <returns>驗證碼字符</returns>
        public static string CreateVerificationText(int length)
        {
            char[] _verification = new char[length];
            char[] _dictionary = { 'A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', 'I', 'J', 'K', 'L', 'M', 'N', 'O', 'P', 'Q', 'R', 'S', 'T', 'U', 'V', 'W', 'X', 'Y', 'Z', 'a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'i', 'j', 'k', 'l', 'm', 'n', 'o', 'p', 'q', 'r', 's', 't', 'u', 'v', 'w', 'x', 'y', 'z', '0', '1', '2', '3', '4', '5', '6', '7', '8', '9' };
            Random _random = new Random();
            for (int i = 0; i < length; i++) { _verification[i] = _dictionary[_random.Next(_dictionary.Length - 1)]; }
            return new string(_verification);
        }

根據驗證碼生成圖片CreateVerificationImage()

思路是使用GDI+創建畫布,使用偽隨機數生成器生成漸變畫刷,然后創建漸變文字。

/// <summary>
        /// 創建驗證碼圖片
        /// </summary>
        /// <param name="verificationText">驗證碼字符串</param>
        /// <param name="width">圖片寬度</param>
        /// <param name="height">圖片長度</param>
        /// <returns>圖片</returns>
        public static Bitmap CreateVerificationImage(string verificationText, int width, int height)
        {
            Pen _pen= new Pen(Color.Black);
            Font _font = new Font("Arial", 14, FontStyle.Bold);
            Brush _brush = null;
            Bitmap _bitmap = new Bitmap(width,height);
            Graphics _g = Graphics.FromImage(_bitmap);
            SizeF _totalSizeF = _g.MeasureString(verificationText,_font);
            SizeF _curCharSizeF;
            PointF _startPointF = new PointF((width-_totalSizeF.Width)/2,(height-_totalSizeF.Height)/2);
            //隨機數產生器
            Random _random =new Random();
            _g.Clear(Color.White);
            for(int i=0;i<verificationText.Length;i++)
            {
                _brush = new LinearGradientBrush(new Point(0,0),new Point(1,1),Color.FromArgb(_random.Next(255),_random.Next(255),_random.Next(255)),Color.FromArgb(_random.Next(255),_random.Next(255),_random.Next(255)));
                _g.DrawString(verificationText[i].ToString(),_font,_brush,_startPointF);
                _curCharSizeF = _g.MeasureString(verificationText[i].ToString(),_font);
                _startPointF.X+= _curCharSizeF.Width;
            }
            _g.Dispose();
            return _bitmap;
        }

User控制器action中保存驗證碼並返回圖片

首先添加User控制器,在Member區域中添加控制器UserController。在控制器中寫一個VerificationCode方法。過程是:在方法中我們先創建6位驗證碼字符串->使用CreateVerificationImage創建驗證碼圖片->把圖片寫入OutputStream中->把驗證碼字符串寫入TempData中。

保存在TempData中和Session中的區別:TempData只傳遞一次,也就是傳遞到下一個action后,action代碼執行完畢就會銷毀,Session會持續保存,所以驗證碼用TempData比較合適。

/// <summary>
        /// 驗證碼
        /// </summary>
        /// <returns></returns>
        public ActionResult VerificationCode()
        {
            string verificationCode = Security.CreateVerificationText(6);
            Bitmap _img = Security.CreateVerificationImage(verificationCode, 160, 30);
            _img.Save(Response.OutputStream, System.Drawing.Imaging.ImageFormat.Jpeg);
            TempData["VerificationCode"] = verificationCode.ToUpper();
            return null;
        }

我們看看生成圖驗證碼效果image

2、Sha256加密

在COmmon項目的Security類中添加靜態方法Sha256(string plainText)

/// <summary>
        /// 256位散列加密
        /// </summary>
        /// <param name="plainText">明文</param>
        /// <returns>密文</returns>
        public static string Sha256(string plainText)
        {
            SHA256Managed _sha256 = new SHA256Managed();
            byte[] _cipherText = _sha256.ComputeHash(Encoding.Default.GetBytes(plainText));
            return Convert.ToBase64String(_cipherText);
        }

四、注冊

在Ninesky.Web.Areas.Member.Models中添加注冊視圖模型

using System.ComponentModel.DataAnnotations;

namespace Ninesky.Web.Areas.Member.Models
{
    public class RegisterViewModel
    {
        /// <summary>
        /// 用戶名
        /// </summary>
        [Required(ErrorMessage = "必填")]
        [StringLength(20, MinimumLength = 4, ErrorMessage = "{2}到{1}個字符")]
        [Display(Name = "用戶名")]
        public string UserName { get; set; }

        /// <summary>
        /// 顯示名
        /// </summary>
        [Required(ErrorMessage = "必填")]
        [StringLength(20, MinimumLength = 2, ErrorMessage = "{2}到{1}個字符")]
        [Display(Name = "顯示名")]
        public string DisplayName { get; set; }

        /// <summary>
        /// 密碼
        /// </summary>
        [Required(ErrorMessage = "必填")]
        [Display(Name = "密碼")]
        [StringLength(20,MinimumLength=6,ErrorMessage="{2}到{1}個字符")]
        [DataType(DataType.Password)]
        public string Password { get; set; }

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

        /// <summary>
        /// 郵箱
        /// </summary>
        [Required(ErrorMessage = "必填")]
        [Display(Name = "郵箱")]
        [DataType(DataType.EmailAddress,ErrorMessage="Email格式不正確")]
        public string Email { get; set; }

        /// <summary>
        /// 驗證碼
        /// </summary>
        [Required(ErrorMessage = "必填")]
        [StringLength(6, MinimumLength = 6, ErrorMessage = "驗證碼不正確")]
        [Display(Name = "驗證碼")]
        public string VerificationCode { get; set; }
    }
}

在UserController中添加Register() action ,並返回直接返回強類型(RegisterViewModel)視圖

/// <summary>
        /// 注冊
        /// </summary>
        /// <returns></returns>
        public ActionResult Register()
        {
            return View();
        }

視圖

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

@{
    ViewBag.Title = "注冊";
    Layout = "~/Views/Shared/_Layout.cshtml";
}


@using (Html.BeginForm()) 
{
    @Html.AntiForgeryToken()
    
    <div class="form-horizontal">
        <h4>用戶注冊</h4>
        <hr />
        @Html.ValidationSummary(true)

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

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

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

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

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

        <div class="form-group">
            @Html.LabelFor(model => model.VerificationCode, new { @class = "control-label col-md-2" })
            <div class="col-md-10">
                @Html.EditorFor(model => model.VerificationCode)
                <img id="verificationcode" title="點擊刷新" src="@Url.Action("VerificationCode")" style="cursor:pointer"  />
                @Html.ValidationMessageFor(model => model.VerificationCode)
            </div>
        </div>
        <div class="checkbox">
            <input type="checkbox" checked="checked" required />我同意 <a href="#">《用戶注冊協議》</a> 
        </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>
}
<script type="text/javascript">
    $("#verificationcode").click(function () {
        $("#verificationcode").attr("src", "@Url.Action("VerificationCode")?" + new Date());
    })
</script>

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

再在用戶控制器中添加public ActionResult Register(RegisterViewModel register)用來處理用戶提交的注冊數據

[HttpPost]
        [ValidateAntiForgeryToken]
        public ActionResult Register(RegisterViewModel register)
        {
            if (TempData["VerificationCode"] == null || TempData["VerificationCode"].ToString() != register.VerificationCode.ToUpper())
            {
                ModelState.AddModelError("VerificationCode", "驗證碼不正確");
                return View(register);
            }
            if(ModelState.IsValid)
            {

                if (userService.Exist(register.UserName)) ModelState.AddModelError("UserName", "用戶名已存在");
                else
                {
                    User _user = new User()
                    {
                        UserName = register.UserName,
                        //默認用戶組代碼寫這里
                        DisplayName = register.DisplayName,
                        Password = Security.Sha256(register.Password),
                        //郵箱驗證與郵箱唯一性問題
                        Email = register.Email,
                        //用戶狀態問題
                        Status = 0,
                        RegistrationTime = System.DateTime.Now
                    };
                    _user = userService.Add(_user);
                    if (_user.UserID > 0)
                    {
                        return Content("注冊成功!");
                        //AuthenticationManager.SignIn();
                    }
                    else { ModelState.AddModelError("", "注冊失敗!"); }
                }
            }
            return View(register);
        }

代碼中很多根用戶設置相關的內容先不考慮,等做到用戶設置時在會后來修改。注冊失敗時返回視圖並顯示錯誤;成功時返回視圖注冊成功,等下次做用戶登錄時可以讓用戶注冊完畢直接進行登錄。在運行項目,瀏覽器地址中輸入http://localhost:52051/Member/User/register,看看效果。

image

點擊注冊,注冊成功。

四、總結

一個簡單的用戶注冊完成了,主要有驗證碼、sha256加密、注冊視圖模型、驗證用戶提交數據並保存等步驟。后面就是用戶注冊,注冊會用到ClaimsIdentity和HttpContext.GetOwinContext().Authentication.SignIn();


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM