ASP.NET MVC4.0+EF+LINQ+bui+网站+角色权限管理系统(6)
快过年了,公司事情忙,好几天没有继续写博客,今天开始写账户模块系统登录,账户管理以及登录日志,
首先新建登录日志数据表:
USE [MVCSystem] GO /****** Object: Table [dbo].[UsersLoginLogs] Script Date: 01/29/2016 15:28:02 ******/ SET ANSI_NULLS ON GO SET QUOTED_IDENTIFIER ON GO CREATE TABLE [dbo].[UsersLoginLogs]( [UsersLoginLogID] [int] IDENTITY(1,1) NOT NULL, [UsersID] [int] NULL, [LoginAddress] [nvarchar](50) NULL, [LoginIP] [nvarchar](50) NULL, [LoginTime] [datetime2](7) NULL, [LoginInfo] [nvarchar](50) NULL, CONSTRAINT [PK_UsersLoginLogs] PRIMARY KEY CLUSTERED ( [UsersLoginLogID] ASC )WITH (PAD_INDEX = OFF, STATISTICS_NORECOMPUTE = OFF, IGNORE_DUP_KEY = OFF, ALLOW_ROW_LOCKS = ON, ALLOW_PAGE_LOCKS = ON) ON [PRIMARY] ) ON [PRIMARY] GO
然后更改模型:AccountModels.cs
using System; using System.Collections.Generic; using System.ComponentModel.DataAnnotations; using System.ComponentModel.DataAnnotations.Schema; using System.Data.Entity; using System.Globalization; using System.Web.Security; namespace MVCSystem.Web.Models { public class RegisterExternalLoginModel { [Required] [Display(Name = "用户名")] public string UserName { get; set; } public string ExternalLoginData { get; set; } } public class LocalPasswordModel { [Required] [DataType(DataType.Password)] [Display(Name = "当前密码")] public string OldPassword { get; set; } [Required] [StringLength(100, ErrorMessage = "{0} 必须至少包含 {2} 个字符。", MinimumLength = 6)] [DataType(DataType.Password)] [Display(Name = "新密码")] public string NewPassword { get; set; } [DataType(DataType.Password)] [Display(Name = "确认新密码")] [Compare("NewPassword", ErrorMessage = "新密码和确认密码不匹配。")] public string ConfirmPassword { get; set; } } public class LoginModel { [Required] [Display(Name = "用户名")] public string UserName { get; set; } [Required] [DataType(DataType.Password)] [Display(Name = "密码")] public string Password { get; set; } /// <summary> /// 验证码 /// </summary> [Required(ErrorMessage = "请输入验证码")] [StringLength(6, MinimumLength = 6, ErrorMessage = "验证码错误")] public string ValidateCode { get; set; } [Display(Name = "记住我?")] public bool RememberMe { get; set; } } public class RegisterModel { [Required] [Display(Name = "用户名")] public string UserName { get; set; } [Required] [StringLength(100, ErrorMessage = "{0} 必须至少包含 {2} 个字符。", MinimumLength = 6)] [DataType(DataType.Password)] [Display(Name = "密码")] public string Password { get; set; } [DataType(DataType.Password)] [Display(Name = "确认密码")] [Compare("Password", ErrorMessage = "密码和确认密码不匹配。")] public string ConfirmPassword { get; set; } } public class ExternalLogin { public string Provider { get; set; } public string ProviderDisplayName { get; set; } public string ProviderUserId { get; set; } } }
创建登录日志模型:M_UsersLoginLogs.cs
using System; using System.Collections.Generic; using System.ComponentModel.DataAnnotations; using System.ComponentModel.DataAnnotations.Schema; using System.Linq; using System.Web; namespace MVCSystem.Web.Models { [Table("UsersLoginLogs")] public class M_UsersLoginLogs { [Key] public int UsersLoginLogID { get; set; } public int UsersID { get; set; } public string LoginAddress { get; set; } public string LoginIP { get; set; } public DateTime? LoginTime { get; set; } public string LoginInfo { get; set; } } }
同样的,MVCSystemContext.cs加上 public DbSet<M_UsersLoginLogs> DB_UsersLoginLogs { get; set; }这句,然后创建账户管理的控制器
AccountController.cs
using System; using System.Collections.Generic; using System.Linq; using System.Transactions; using System.Web; using System.Web.Mvc; using System.Web.Security; using DotNetOpenAuth.AspNet; using Microsoft.Web.WebPages.OAuth; using WebMatrix.WebData; using MVCSystem.Web.Filters; using MVCSystem.Web.Models; using MVCSystem.Web.Common; using System.Drawing; using System.Drawing.Drawing2D; namespace MVCSystem.Web.Areas.Admin.Controllers { [Authorize] [InitializeSimpleMembership] public class AccountController : BaseController { // // GET: /Account/Login [AllowAnonymous] public ActionResult Login(string returnUrl) { ViewBag.ReturnUrl = returnUrl; return View(); } // // POST: /Account/Login [HttpPost] [AllowAnonymous] [ValidateAntiForgeryToken] public ActionResult Login(LoginModel model, M_UsersLoginLogs userLogs, string returnUrl) { if (Session["ValidateImgCode"] != null && string.Equals(Session["ValidateImgCode"].ToString(), model.ValidateCode, StringComparison.OrdinalIgnoreCase)) { if (ModelState.IsValid && WebSecurity.Login(model.UserName, model.Password, persistCookie: model.RememberMe)) { var usersId = db.DB_UserProfiles.Single(u => u.UserName == model.UserName).UserId;//登录日志 userLogs.UsersID = usersId; userLogs.LoginAddress = IpHelper.GetAdrByIp(IpHelper.GetRealIP()); userLogs.LoginIP = IpHelper.GetRealIP(); userLogs.LoginTime = DateTime.Now; userLogs.LoginInfo = IpHelper.GetBrowerVersion(); db.DB_UsersLoginLogs.Add(userLogs); db.SaveChanges(); return RedirectToLocal(returnUrl); } // 如果我们进行到这一步时某个地方出错,则重新显示表单 ModelState.AddModelError("", "提供的用户名或密码不正确。"); return View(model); } else { ModelState.AddModelError("", "验证码错误"); return View(model); } } // // POST: /Account/LogOff [HttpPost] [ValidateAntiForgeryToken] public ActionResult LogOff() { WebSecurity.Logout(); return RedirectToAction("Login", "Account"); } #region 验证码 /// <summary> /// 功能:返回验证码图片 /// </summary> /// <returns></returns> [AllowAnonymous] public ActionResult ValidateImg() { Color color1 = new Color(); //---------产生随机6位字符串 Random ran = new Random(); char[] c = new char[62]; char[] ou = new char[6]; int n = 0; for (int i = 65; i < 91; i++) { c[n] = (char)i; n++; } for (int j = 97; j < 123; j++) { c[n] = (char)j; n++; } for (int k = 48; k < 58; k++) { c[n] = (char)k; n++; } foreach (char ch in c) { Console.WriteLine(ch); } string outcode = ""; for (int h = 0; h < 6; h++) { ou[h] = c[ran.Next(62)]; outcode += ou[h].ToString(); } // Session["ValidateImgCode"] = outcode; //1.创建一个新的图片,大小为(输入的字符串的长度*12),22 System.Drawing.Bitmap bmap = new System.Drawing.Bitmap(outcode.Length * 18, 25); //2.定义画图面板,基于创建的新图片来创建 System.Drawing.Graphics g = System.Drawing.Graphics.FromImage(bmap); //3.由于默认的画图面板背景是黑色,所有使用clear方法把背景清除,同时把背景颜色设置为白色 g.Clear(System.Drawing.Color.White); // 画图片的背景噪音线 for (int i = 0; i < 25; i++) { int x1 = ran.Next(bmap.Width); int x2 = ran.Next(bmap.Width); int y1 = ran.Next(bmap.Height); int y2 = ran.Next(bmap.Height); g.DrawLine(new Pen(color1), x1, y1, x2, y2); } // 画图片的前景噪音线 for (int i = 0; i < 100; i++) { int x = ran.Next(bmap.Width); int y = ran.Next(bmap.Height); bmap.SetPixel(x, y, Color.FromArgb(ran.Next())); } //4.使用DrawString 方法把要输出的字符串输出到画板上。输出的字符从参数(outcode)内获得。 Font font = new Font("Arial", 14, FontStyle.Bold | FontStyle.Italic); LinearGradientBrush brush = new LinearGradientBrush(new Rectangle(0, 0, bmap.Width, bmap.Height), Color.Blue, Color.DarkRed, 1.2f, true); g.DrawString(outcode, font, brush, 0, 0); //5.定义一个内存流,把新创建的图片保存到内存流内,这样就不用保存到磁盘上,提高了速度。 System.IO.MemoryStream ms = new System.IO.MemoryStream(); //6.把新创建的图片保存到内存流中,格式为jpeg的类型 bmap.Save(ms, System.Drawing.Imaging.ImageFormat.Jpeg); //7.输出这张图片,由于此页面的 ContentType="image/jpeg" 所以会输出图片到客户端。同时输出是以字节输出,所以要把内存流转换为字节序列,使用ToArray()方法。 Response.BinaryWrite(ms.ToArray()); return View(); } #endregion // // GET: /Account/Manage /// <summary> /// 密码重置 /// </summary> /// <param name="message"></param> /// <returns></returns> public ActionResult Manage(ManageMessageId? message) { ViewBag.StatusMessage = message == ManageMessageId.ChangePasswordSuccess ? "<script>alert('密码修改成功!');</script>" : ""; ViewBag.HasLocalPassword = OAuthWebSecurity.HasLocalAccount(WebSecurity.GetUserId(User.Identity.Name)); ViewBag.ReturnUrl = Url.Action("Manage"); return View(); } // // POST: /Account/Manage [HttpPost] [ValidateAntiForgeryToken] public ActionResult Manage(LocalPasswordModel model) { bool hasLocalAccount = OAuthWebSecurity.HasLocalAccount(WebSecurity.GetUserId(User.Identity.Name)); ViewBag.HasLocalPassword = hasLocalAccount; ViewBag.ReturnUrl = Url.Action("Manage"); if (hasLocalAccount) { if (ModelState.IsValid) { // 在某些出错情况下,ChangePassword 将引发异常,而不是返回 false。 bool changePasswordSucceeded; try { changePasswordSucceeded = WebSecurity.ChangePassword(User.Identity.Name, model.OldPassword, model.NewPassword); } catch (Exception) { changePasswordSucceeded = false; } if (changePasswordSucceeded) { return RedirectToAction("Manage", new { Message = ManageMessageId.ChangePasswordSuccess }); } else { ModelState.AddModelError("", "当前密码不正确或新密码无效!"); } } } else { // 用户没有本地密码,因此将删除由于缺少 // OldPassword 字段而导致的所有验证错误 ModelState state = ModelState["OldPassword"]; if (state != null) { state.Errors.Clear(); } if (ModelState.IsValid) { try { WebSecurity.CreateAccount(User.Identity.Name, model.NewPassword); return RedirectToAction("Manage", new { Message = ManageMessageId.SetPasswordSuccess }); } catch (Exception) { ModelState.AddModelError("", String.Format("无法创建本地帐户。可能已存在名为“{0}”的帐户。", User.Identity.Name)); } } } // 如果我们进行到这一步时某个地方出错,则重新显示表单 return View(model); } #region 帮助程序 private ActionResult RedirectToLocal(string returnUrl) { if (Url.IsLocalUrl(returnUrl)) { return Redirect(returnUrl); } else { return RedirectToAction("Index", "Home"); } } public enum ManageMessageId { ChangePasswordSuccess, SetPasswordSuccess, RemoveLoginSuccess, } internal class ExternalLoginResult : ActionResult { public ExternalLoginResult(string provider, string returnUrl) { Provider = provider; ReturnUrl = returnUrl; } public string Provider { get; private set; } public string ReturnUrl { get; private set; } public override void ExecuteResult(ControllerContext context) { OAuthWebSecurity.RequestAuthentication(Provider, ReturnUrl); } } #endregion } }
视图Account/Manage.cshtml【账户管理】
@model MVCSystem.Web.Models.LocalPasswordModel @{ ViewBag.Title = "Manage"; Layout = "~/Areas/Admin/Views/Shared/_Layout_Admin.cshtml"; } @using (Html.BeginForm()) { @Html.AntiForgeryToken() @Html.ValidationSummary(true) <div class="container"> @Html.Raw(ViewBag.StatusMessage) <table class="table table-bordered" > <tr> <td width="10%" class="tableleft">旧密码</td> <td> @Html.PasswordFor(m => m.OldPassword) @Html.ValidationMessageFor(model => model.OldPassword)</td> </tr> <tr> <td width="10%" class="tableleft">新密码</td> <td> @Html.PasswordFor(m => m.NewPassword) @Html.ValidationMessageFor(model => model.NewPassword)</td> </tr> <tr> <td width="10%" class="tableleft">确认密码</td> <td> @Html.PasswordFor(m => m.ConfirmPassword) @Html.ValidationMessageFor(model => model.ConfirmPassword)</td> </tr> <tr> <td class="tableleft"></td> <td> <button type="submit" class="button button-primary">修改密码</button> </td> </tr> </table> </div> }
然后添加账户管理菜单,无需多说,这个大家懂
接下来是登录功能,首先看看视图Account/Login.cshtml
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd"> <html xmlns="http://www.w3.org/1999/xhtml" xml:lang="en"> @model MVCSystem.Web.Models.LoginModel <head> <meta http-equiv="Content-Type" content="text/html;charset=UTF-8"/> <title>登录</title> <link href="~/Content/Css/style.css" rel="stylesheet" /> <link href="~/Content/Css/login.css" rel="stylesheet" /> <style type="text/css"> .inner { position:relative; } .inner .field-validation-error { position:absolute;top:-10px;left:100px; color:#f00; } #RememberMe { width:15px;height:15px; margin-top:7px; } .validation-summary-errors { position:absolute; top:10px; right:50%; color:#f00; } #ValidateCode { width:100px; } </style> </head> <body> @using (Html.BeginForm(new { ReturnUrl = ViewBag.ReturnUrl })) { <div class="w_body"> <div class="bodyBg"><img src="~/Content/Images/loginBodyBg.png" /></div> <div class="whiteBg"></div> <div class="center"> <div class="titleBg"></div> <div class="login"> @Html.AntiForgeryToken() @Html.ValidationSummary(true) <div class="form"> <div class="inner p_clearfix"><label for="user">账 号:</label> @Html.TextBoxFor(m => m.UserName) @Html.ValidationMessageFor(m => m.UserName)</div> <div class="inner p_clearfix"><label for="pword">密 码:</label>@Html.PasswordFor(m => m.Password) @Html.ValidationMessageFor(m => m.Password)</div> <div class="inner p_clearfix"><label for="pword">验证码:</label> @Html.TextBoxFor(m => m.ValidateCode, new { @placeholder = "6位数的验证码" }) <img src="/Admin/Account/ValidateImg" id="validimg" height="34" alt="验证码" title="看不清?,换一张" style="cursor:pointer;float:left;" onclick="ShowValidate()" /> @Html.ValidationMessageFor(m => m.ValidateCode) </div> <div class="inner p_clearfix checkboxCom"> <label for="pword">记住我:</label> @Html.CheckBoxFor(m => m.RememberMe) </div> <button type="submit" title="登录" class="loginBtn p_i"></button> </div> </div> <div class="copyRight">Copyright ©2012 网站角色权限管理系统 滇ICP备xxxxxxx号</div> </div> </div> } </body> </html> <script src="~/Content/assets/js/jquery-1.8.1.min.js"></script> <script type="text/javascript"> function ShowValidate() { $("#validimg").get(0).src = '/Admin/Account/ValidateImg?time=' + (new Date()).getTime(); } </script>
他们用的css样式表,以及背景图,下面给出来,大家复制下去就可以用了
Content/Css/login.css
@charset "utf-8"; .w_body .bodyBg{ position: fixed; bottom: 0; left: 0; z-index: -999; overflow: hidden; width: 100%; height: 100%; min-height: 100%; _position: absolute;} .w_body .bodyBg img { width: 100%; height: 100%;} .w_body { background: #286ea7 url(/Content/Images/loginBodyBg.png) center no-repeat; height:100%; width: 100%; position: fixed; left: 0; bottom: 0; z-index: -999;} .w_body .whiteBg { width: 100%; height: 217px; position: relative; top: 245px; background-color: #fff;} .w_body .center { width: 595px; margin: 0 auto; position: relative; top: -217px;} .w_body .titleBg { width: 392px; height: 55px; position: absolute; top: 140px; left: 100px; background-image: url(/Content/Images/title.png); background-repeat: no-repeat;} .w_body .login { width: 595px; height: 232px; position: absolute; top: 245px; left: 0; right: 0; margin: auto; background-image: url(/Content/Images/loginBg.png); background-repeat: no-repeat;} .w_body .login .form { width: 360px; float: left; margin: 50px 0 0 40px;} .w_body .login .form .inner { margin: 10px 0; padding-bottom: 35px; line-height: 28px;} .w_body .login .form .inner label { display: block; float: left; width: 90px; font-size: 14px; color: #525252; text-align: right;} .w_body .login .form .inner input { float: left; border: 1px solid #ddd; width: 220px; height: 20px; line-height: 20px; padding: 5px;} .w_body .login .form .inner .w { width: 80px;} .w_body .login .form .inner a { margin-left: 20px;} .w_body .login .form .loginBtn {cursor:pointer; display: block; width: 135px; height: 135px;border:0 none; position: absolute; top: 50px; right: 70px; background-image: url(/Content/Images/loginBtn.png); background-repeat: no-repeat;} .w_body .copyRight { width: 595px; color: #fff; text-align: center; position: absolute; top: 500px; left: 0; right: 0; margin: auto;}
Content/Css/style.css
@charset "utf-8"; body{ font-size: 13px; } select, textarea, input[type="text"], input[type="password"], input[type="datetime"], input[type="datetime-local"], input[type="date"], input[type="month"], input[type="time"], input[type="week"], input[type="number"], input[type="email"], input[type="url"], input[type="search"], input[type="tel"], input[type="color"], .uneditable-input { padding: 1px; } select { height: 24px; } .radio{ font-size: 11px; margin-bottom: 4px; } .nav{ margin-bottom: 5px; } form{ margin: 0 0 5px; } .page{ text-align: right;margin-right:25px;margin-top:5px; } .page a{ margin-left: 5px; } .page .current{ margin-left: 5px; color: red; } .table td input[type="checkbox"]{ padding: 0; margin: 0; } .table th input[type="checkbox"]{ padding: 0; margin: 0; } .table td, .table th{ padding-top: 8px; padding-bottom: 4px; line-height:20px; } .table th{ background-color:#eaeaea; } .definewidth{ width:96%; margin:auto; } .m10{ margin-top:10px; } .m20{ padding-top:20px; } .bui-grid-cell-text a { padding-right:10px; } .tableleft{ text-align:right; padding-left:5px; background-color:#f5f5f5; } .text-center{ text-align: center;} .text-left{ text-align: left;} .text-right{ text-align: right;} /*浮动样式 S*/ .L { float: left;} .R { float: right;} /*浮动样式 E*/ /*清除浮动 S*/ .clearfix { #zoom:1; } .clearfix:after { content:'.'; height:0; line-height:0; display:block; clear:both; visibility:hidden; } .clear{ clear:both;margin:0 auto; overflow:hidden;} /*清除浮动 E*/ /*formValidator�����֤*/ .onShow,.onFocus,.onError,.onCorrect,.onLoad,.onTime{display:inline-block;display:-moz-inline-stack;zoom:1;*display:inline; vertical-align:middle;background:url(../Images/msg_bg.png) no-repeat; color:#444;line-height:18px;padding:2px 10px 2px 23px; margin-left:10px;_margin-left:5px} .onShow{background-position:3px -147px;border-color:#40B3FF;color:#959595} .onFocus{background-position:3px -147px;border-color:#40B3FF;} .onError{background-position:3px -47px;border-color:#40B3FF; color:red} .onCorrect{background-position:3px -247px;border-color:#40B3FF;} .onLamp{background-position:3px -200px} .onTime{background-position:3px -1356px} /*上传附件*/ .fileCom { position:relative; float:left; width:220px; padding:3px 5px; background-color:#6CB5F4; border-radius:4px; margin-right:10px; overflow:hidden; } .fileStr { position:absolute; left:65px; cursor:pointer; width:500px; height:20px; line-height:20px; background-color:#6CB5F4; padding:5px; top:0; color:#fff; } #textFile { width:220px; overflow:hidden; } .imgCom { float:left;padding-right:10px; }
以及背景图:链接: http://pan.baidu.com/s/1ge0e4GN 密码: urqv。下载后放到Content/Images/
这里的登录页面需要输入验证码,我们需要添加一个空的视图来临时存放每次生成的验证码图片:
Account/ValidateImg.cshtml
@{
Layout = null;
}
接着需要创建一个登录日志ip地址获取的帮助类,我们把它放到common这个存放公共类的文件夹中:
IpHelper.cs
using System; using System.Collections.Generic; using System.IO; using System.Linq; using System.Net; using System.Text; using System.Text.RegularExpressions; using System.Web; namespace MVCSystem.Web.Common { public class IpHelper { /// <summary> /// 通过IP得到IP所在地省市(Porschev) /// </summary> /// <param name="ip"></param> /// <returns></returns> public static string GetAdrByIp(string ip) { string url = "http://www.cz88.net/ip/?ip=" + ip; string regStr = "(?<=<span\\s*id=\\\"cz_addr\\\">).*?(?=</span>)"; //得到网页源码 string html = GetHtml(url); Regex reg = new Regex(regStr, RegexOptions.None); Match ma = reg.Match(html); html = ma.Value; string[] arr = html.Split(' '); return arr[0]; } /// <summary> /// 获取当前Ip所在地址 /// </summary> /// <returns></returns> public static string GetAdrByIp() { string address = GetAdrByIp(GetRealIP()); return address == "" ? "中国" : address; } /// <summary> /// 获取HTML源码信息(Porschev) /// </summary> /// <param name="url">获取地址</param> /// <returns>HTML源码</returns> public static string GetHtml(string url) { string str = ""; try { Uri uri = new Uri(url); WebRequest wr = WebRequest.Create(uri); Stream s = wr.GetResponse().GetResponseStream(); StreamReader sr = new StreamReader(s, Encoding.Default); str = sr.ReadToEnd(); } catch (Exception e) { } return str; } /// <summary> /// 获得客户端的IP /// </summary> /// <returns>当前客户端的IP</returns> public static string GetRealIP() { string strIP = HttpContext.Current.Request.ServerVariables["HTTP_X_FORWARDED_FOR"]; if (strIP == null || strIP.Length < 1) { strIP = HttpContext.Current.Request.UserHostAddress; } if (strIP == null || strIP.Length < 1) { return "0.0.0.0"; } return strIP; } /// <summary> /// 获取浏览器的版本以及名称 /// </summary> /// <returns></returns> public static string GetBrowerVersion() { string browerVersion = string.Empty; if (System.Web.HttpContext.Current != null) { System.Web.HttpBrowserCapabilities browser = System.Web.HttpContext.Current.Request.Browser; if (browser != null) { browerVersion = browser.Browser + browser.Version; } } return browerVersion; } } }
最后大道登录后才能访问页面在各个视图中添加授权标识 [Authorize]
namespace MVCSystem.Web.Areas.Admin.Controllers { [Authorize] public class HomeController : BaseController {
大功告成,接下来看运行效果:
然后根据之前添加的用户账户
可以登录了,然后查看登录日志数据表,可以看到一条登录日志
登录日志管理,这里就不写了,大家可以动手写下!
源码下载:http://www.yealuo.com/Sccnn/Detail?KeyValue=2f926407-f80b-4bff-a729-949a53efed7b
作者:boyzi007
出处:http://www.cnblogs.com/boyzi/
QQ:470797533
QQ交流群:364307742
本文版权归作者和博客园共有,欢迎转载,但未经作者同意必须保留此段声明,且在文章页面明显位置给出原文连接,否则保留追究法律责任的权利。