上一次把基本框架搭建起来了,这次开始整Web部分,终于可以看到界面了小激动一下。web项目部分从用户功能开始,基本有注册,登录、注销、查找、查看、删除等涉及Member区域和Manage区域。

目录:

ASP.NET MVC5 网站开发实践 - 概述

ASP.NET MVC5 网站开发实践(一) - 项目框架

ASP.NET MVC5 网站开发实践(一) - 框架(续) 模型、数据存储、业务逻辑

 

 

一、默认Web项目的更改

在上一次中我们创建了一个使用个人用户账户的mvc项目,通过分析自动生成的用户管理代码(见《VS2013中web项目中自动生成的ASP.NET Identity代码思考》),我觉用户这部分还是自己做,所以删除自动生成的用户相关代码。

 

二、添加Member区域

在web项目上点右键 添加 区域Member。

添加Home控制器,选择MVC5控制器-空

我们给public ActionResult Index()添加一个视图,代码很简单就是显示下用户名

@{
ViewBag.Title = "会员中心";
} <h2>欢迎你!@User.Identity.Name
</h2>

我们先运行一下,出错啦。

这是因为项目中有两个名为Home的控制器,必须在路由中加上命名空间。先打开区域中的MemberAreaRegistration添加命名空间。

再打开项目中的RouteConfig,添加命名空间

再刷新浏览器,可以正常显示。

再添加用户控制器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类。 如下图蓝色框为修改部分,红框为新增加

三、验证码及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;
}

我们看看生成图验证码效果

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,看看效果。

点击注册,注册成功。

四、总结

一个简单的用户注册完成了,主要有验证码、sha256加密、注册视图模型、验证用户提交数据并保存等步骤。后面就是用户注册,注册会用到ClaimsIdentity和HttpContext.GetOwinContext().Authentication.SignIn();

ASP.NET MVC5 网站开发实践(二) Member区域 - 用户部分(1)用户注册的更多相关文章

  1. ASP.NET MVC5 网站开发实践(二) Member区域 - 用户部分(3)修改资料、修改密码

    在上一篇博客中实现了用户的注销和登录,其实代码里落了点东西,就是用户登录要更新最后一次登录时间和登录IP,这次补上.今天做修改资料和修改密码,TryUpdateModel是新用到的东西. 目录: AS ...

  2. ASP.NET MVC5 网站开发实践(二) Member区域 - 用户部分(2)用户登录、注销

    上次实现了用户注册,这次来实现用户登录,用到IAuthenticationManager的SignOut.SignIn方法和基于声明的标识.最后修改用户注册代码实现注册成功后直接登录. 目录: ASP ...

  3. ASP.NET MVC5 网站开发实践(二) Member区域–管理列表、回复及删除

    本来想接着上次把这篇写完的,没想到后来工作的一些事落下了,放假了赶紧补上. 目录: ASP.NET MVC5 网站开发实践 - 概述 ASP.NET MVC5 网站开发实践(一) - 项目框架 ASP ...

  4. ASP.NET MVC5 网站开发实践(二) Member区域–我的咨询列表及添加咨询

    上次把咨询的架构搭好了,现在分两次来完成咨询:1.用户部分,2管理部分.这次实现用户部分,包含两个功能,查看我的咨询和进行咨询. 目录: ASP.NET MVC5 网站开发实践 - 概述 ASP.NE ...

  5. ASP.NET MVC5 网站开发实践(二) Member区域 - 咨询管理的架构

    咨询.留言.投诉等功能是网站应具备的基本功能,可以加强管理员与用户的交流,在上次完成文章部分后,这次开始做Member区域的咨询功能(留言.投诉都是咨询).咨询跟文章非常相似,而且内容更少.更简单. ...

  6. ASP.NET MVC5 网站开发实践(二) Member区域 - 修改及删除文章

    上次做了显示文章列表,再实现修改和删除文章这部分内容就结束了,这次内容比较简单,由于做过了添加文章,修改文章非常类似,就是多了一个TryUpdateModel部分更新模型数据.   目录: ASP.N ...

  7. ASP.NET MVC5 网站开发实践(二) Member区域 - 全部文章列表

    显示文章列表分两块,管理员可以显示全部文章列表,一般用户只显示自己的文章列表.文章列表的显示采用easyui-datagrid.后台需要与之对应的action返回json类型数据   目录 ASP.N ...

  8. ASP.NET MVC5 网站开发实践(二) Member区域 - 添加文章

    上次把架构做好了,这次做添加文章.添加文章涉及附件的上传管理及富文本编辑器的使用,早添加文章时一并实现. 要点: 富文本编辑器采用KindEditor.功能很强大,国人开发,LGPL开源,自己人的好东 ...

  9. ASP.NET MVC5 网站开发实践(二) Member区域 - 文章管理架构

    上次把member的用户部分完成,现在开始做文章管理部分.文章部分根据涉及显示现实文章列表,发布文章,修改文章,删除文章等功能.最终的实现目标是使用权限来控制用户是否能进行相应操作,管理员权限的会显示 ...

随机推荐

  1. MongoDB 由于目标计算机积极拒绝,无法连接 2014-07-25T11:00:48.634+0800 warning: Failed to connect to 127.0.0.1:27017, reason: errno:10061

    转载自:http://www.cnblogs.com/xiaoit/p/3867573.html 1:启动MongoDB 2014-07-25T11:00:48.634+0800 warning: F ...

  2. 李洪强iOS经典面试题155 - const,static,extern详解(面试必备)

    李洪强iOS经典面试题155 - const,static,extern详解(面试必备) 一.const与宏的区别(面试题): const简介:之前常用的字符串常量,一般是抽成宏,但是苹果不推荐我们抽 ...

  3. VS2012中丢失ArcGIS模板的解决方法

    VS2012中丢失ArcGIS模板的解决方法 由于ArcGIS10.0(for .NET)默认是用VS2010作为开发工具的,所以在先安装VS2012后装ArcGIS10.0 桌面版及ArcObjec ...

  4. php中redis的安装

    1.当你在使用php时出现下面的问题 2.通过phpinfo()查看php的版本 我的是php5.6版本 3.查看需要下载的redis的版本 4.点击下面的额链接下载redis:http://wind ...

  5. oracle统计用户下面所有的表,并显示每个表的行数

    declare  t_count   number(10);  t_str VARCHAR2(500);  cursor t_tables is select table_name from user ...

  6. 如何给GridView添加网格

    如何给gridview的单元格加上分割线 原文链接:http://www.jcodecraeer.com/a/anzhuokaifa/androidkaifa/2013/1227/1582.html ...

  7. 用javascript实现一个2048游戏

    早就想自己写一个2048游戏了,昨晚闲着没事,终于写了一个 如下图,按方向键开始玩吧. 如果觉得操作不方便,请直接打开链接玩吧: http://gujianbo.1kapp.com/2048/2048 ...

  8. ASP.NET MVC 防止 CSRF 的方法

    MVC中的Html.AntiForgeryToken()是用来防止跨站请求伪造(CSRF:Cross-site request forgery)攻击的一个措施,它跟XSS(XSS又叫CSS:Cross ...

  9. [.net 面向对象程序设计进阶] (18) 多线程(Multithreading)(三) 利用多线程提高程序性能(下)

    [.net 面向对象程序设计进阶] (18) 多线程(Multithreading)(二) 利用多线程提高程序性能(下) 本节导读: 上节说了线程同步中使用线程锁和线程通知的方式来处理资源共享问题,这 ...

  10. 玩转Windows服务系列——Windows服务小技巧

    伴随着研究Windows服务,逐渐掌握了一些小技巧,现在与大家分享一下. 将Windows服务转变为控制台程序 由于默认的Windows服务程序,编译后为Win32的窗口程序.我们在程序启动或运行过程 ...