mvc5+ef6+Bootstrap 项目经验-身份验证和权限管理
1.mvc5+ef6+Bootstrap 项目心得--身份验证和权限管理
2.mvc5+ef6+Bootstrap 项目心得--WebGrid
最近和朋友完成了一个大单子架构是mvc5+ef6+Bootstrap,用的是vs2015,数据库是sql server2014。朋友做的架构,项目完成后觉得很多值得我学习,在这里总结下一些心得。
创建项目一开始删掉App_Start目录下的IdentityConfig.cs和Startup.Auth.cs文件;清空Modle文件夹,Controller文件夹和相应的View; 删除目录下的ApplicationInsights.config文件和Startup.cs文件。(不使用自带的Identity,太多内容不需要)
修改web.config文件(添加<add key="owin:AutomaticAppStartup" value="false"/>不使用Startup.cs文件来启动项目)
<appSettings> <add key="webpages:Version" value="3.0.0.0" /> <add key="webpages:Enabled" value="false" /> <add key="ClientValidationEnabled" value="true" /> <add key="UnobtrusiveJavaScriptEnabled" value="true" /> <add key="owin:AutomaticAppStartup" value="false"/> <!--去掉创建项目初的Startup.cs文件的设置--> </appSettings>
<modules> <remove name="FormsAuthentication"/> </modules> 这句在web.config里面必须去掉,不然User.Identity不起作用
(不用他们是因为自带的这些内容太冗余)
1.首先介绍数据库这一块,数据库我们是配置的可以使用NuGet命令手动生成和修改的。在项目目录想创建Migrations文件夹,里面添加Configuration.cs文件
internal sealed class Configuration : DbMigrationsConfiguration<AccountContext> { public Configuration() { AutomaticMigrationsEnabled = true; ContextKey = "UserProject.DAL.AccountContext"; } protected override void Seed(AccountContext context) { //base.Seed(context); } }
在Model文件夹下添加AccountContext.cs文件
public class AccountContext:DbContext { public AccountContext():base("AccountContext") { } public DbSet<User> Users { get; set; } protected override void OnModelCreating(DbModelBuilder modelBuilder) { modelBuilder.Conventions.Remove<PluralizingTableNameConvention>(); } }
<connectionStrings> <add name="AccountContext" connectionString="Data Source=(LocalDb)\MSSQLLocalDB;AttachDbFilename=|DataDirectory|\UserProject.mdf;Initial Catalog=UserProject;Integrated Security=True" providerName="System.Data.SqlClient" /> </connectionStrings>
然后 使用vs2015里面的工具-NuGet包管理器-程序包管理控制平台
输入add-migration Initial 按回车,在输入update-database按回车。在App_Data文件夹下就会看到AccountContext数据库了。
2.创建测试用的Model
在Model文件夹下添加User.css文件
public class User { public int ID { get; set; } public string UserName { get; set; } public string Password { get; set; } public Role Role { get; set; } }
public enum Role//角色枚举
{ 管理员 = 1, 员工 = 2, 经理 = 3, 总经理 = 4, 董事长 = 5 }
在ViewModel文件夹中添加Account.cs文件
public class Account { [Required] public string Name { get; set; } [Required] public string Password { get; set; } public string RePassword { get; set; } }
3.创建测试用到的Controller
这里推荐创建BaseController,之后的Controller就继承它来使用
public class BaseController : Controller { public string UserName => User.Identity.Name; public AccountContext db = new AccountContext(); private User _userInfo = null; public User CurrentUserInfo { get { if (_userInfo == null) { var user = db.Users.SingleOrDefault(u => u.UserName == UserName);//此处为了不每次访问用户表可以做一个静态类,里面存放用户表信息. _userInfo = user == null ? null : new User() { ID = user.ID, UserName = user.UserName, Role = user.Role }; } return _userInfo; } }
//验证角色:获取Action的CustomAttributes,过滤角色 protected override void OnActionExecuting(ActionExecutingContext filterContext) { base.OnActionExecuting(filterContext); var authRoleAtt = filterContext.ActionDescriptor.GetCustomAttributes(false).SingleOrDefault(att => att is AuthorizeRoleAttribute) as AuthorizeRoleAttribute; if (authRoleAtt == null && CurrentUserInfo != null) return; if (!authRoleAtt.Roles.Contains(CurrentUserInfo.Role)) { filterContext.Result = View("NoPermission", "_Layout", "您没有权限访问此功能!"); } }
//这里是记log用 protected override void OnActionExecuted(ActionExecutedContext filterContext) { base.OnActionExecuted(filterContext); var msg = $"用户: {CurrentUserInfo?.UserName}, 链接: {Request.Url}"; if (Request.HttpMethod == "POST") msg += $", 数据: {HttpUtility.UrlDecode(Request.Form.ToString())}"; //Log.Debug(msg); } }
public class AuthorizeRoleAttribute : Attribute { public List<Role> Roles { get; set; } public AuthorizeRoleAttribute(params Role[] roles) { Roles = new List<Role>(roles); } }
AdminController继承BaseController
[Authorize] public ActionResult Index() { return View(db.Users.ToList()); } [Authorize, AuthorizeRole(Role.管理员)] public ActionResult Details(int? id) { if (id == null) { return new HttpStatusCodeResult(HttpStatusCode.BadRequest); } User user = db.Users.Find(id); if (user == null) { return HttpNotFound(); } return View(user); }
4.创建Action和VIew
登录页面:
@model UserProject.ViewModels.Account @{ ViewBag.Title = "Login"; } @using (Html.BeginForm("Login", "Admin",FormMethod.Post, new { @class = "form-horizontal", role = "form" })) { @Html.AntiForgeryToken() <hr /> @Html.ValidationSummary(true, "", new { @class = "text-danger" }) <div class="form-group"> @Html.LabelFor(m => m.Name, new { @class = "col-md-2 control-label" }) <div class="col-md-10"> @Html.TextBoxFor(m => m.Name, new { @class = "form-control" }) @Html.ValidationMessageFor(m => m.Name, "", new { @class = "text-danger" }) </div> </div> <div class="form-group"> @Html.LabelFor(m => m.Password, new { @class = "col-md-2 control-label" }) <div class="col-md-10"> @Html.PasswordFor(m => m.Password, new { @class = "form-control" }) @Html.ValidationMessageFor(m => m.Password, "", new { @class = "text-danger" }) </div> </div> <div class="form-group"> <div class="col-md-offset-2 col-md-10"> <input type="submit" value="登录" class="btn btn-primary" /> </div> </div> }
登录的Action:
[AllowAnonymous] public ActionResult Login() { return View(); } [HttpPost, AllowAnonymous] public ActionResult Login(Account model) { if (ModelState.IsValid) { var user = db.Users.SingleOrDefault(t => t.UserName == model.Name && t.Password == model.Password); if (user != null) { FormsAuthentication.SetAuthCookie(model.Name, false);//将用户名放入Cookie中 return RedirectToAction("Index"); } else { ModelState.AddModelError("Name", "用户名不存在!"); } } return View(model); } public ActionResult LogOff() { FormsAuthentication.SignOut(); return RedirectToAction("Login"); }
通过以上方式就完成了用户的登录和权限的控制, 访问Details这个Action的时候必须是管理员角色。
如果需要多个角色使用Action可以:[Authorize, AuthorizeRole(Role.管理员,Role.经理)]
使用此方法登录的用户保存在Cookie里面 :FormsAuthentication.SetAuthCookie(model.Name, false);
在Controller和View里面直接使用User.Identity得到用户名
此仅仅是测试项目,作为完成私活的部分知识点的总结。
- 20楼蔡大卫
- 我的习惯比较传统,喜欢DAL,喜欢ADO.net,EF的东西太过华丽。
- 19楼任意球
- 想问下,这单的项目复杂度高么,怎么报价的,想学习
- 18楼JustYong
- 你这枚举定义有问题啊,如果一个人既是董事长又是总经理,你数据库要怎么保存
- Re: 逸舞梵谷
- @JustYong,这个枚举定义的确有失水准,,1、枚举从0开始,一旦枚举没有赋值成功,那么枚举值默认是管理员,权限略大。,2、枚举没考虑一人多角色,此时应该使用位枚举。,3、这种设计可能没有考虑,当用户大于某个权限时,可以做什么的设计。,,可以设计成,员工 = 1, 经理 = 2, 总经理 = 4, 董事长 = 8, 管理员 = 128
- 17楼春色园
- 我都是直接拉到底部看有没有源码的,结果啥也没有,反对
- Re: 丰叔叔
- @春色园,怎么可能暴露源码!这可是给客户做的项目。
- 16楼最爱晴天
- 是在看不出有什么好的知识点。。。。图也没有。demo也没有,知识也没分享。。。。
- 15楼码农搬砖
- 没有专门的数据访问层 直接在action里面调用EF好么
- Re: 丰叔叔
- @码农搬砖,不需要了,MVC5+ef就没必要在搞个专门的DAL了。而是这是我我的测试小项目。正式项目是分开的。
- 14楼We_Are_Friends!
- “哥们,你好,我用mvc4+ef6+bootstrap,出现这个问题,,asp mvc 生成的两个页面,chrome下,一个内容宽度为1920,另一个内容宽度为1902,导致body显示位置偏左,,,我从没设置过Html/Body的宽度”,,~~~~~~~~~~~~~~~~~~~~~~~,“@ We_Are_Friends!,其它浏览器正常吗”,~~~~~~~~~~~~~~~~~~~~~~~,firefox也是这种情况,IE11显示正常
- 13楼liyunzhi
- 没牵扯到按钮吗
- Re: 丰叔叔
- @liyunzhi,按钮和本文无关吧
- 12楼We_Are_Friends!
- 哥们,你好,我用mvc4+ef6+bootstrap,出现这个问题,,asp mvc 生成的两个页面,chrome下,一个内容宽度为1920,另一个内容宽度为1902,导致body显示位置偏左,,,请问这是为什么,,,谢谢,,,我从没设置过Html/Body的宽度
- Re: 丰叔叔
- @We_Are_Friends!,其它浏览器正常吗
- 11楼ta_wuhen
- 人人上首页
- 10楼loveseejoy
- 删那么多Identity,你创建项目的时候 选择不适用任何验证不就好了
- 9楼leehavin
- 是基于bootstrap,没用第三方的吗?
- Re: 丰叔叔
- @leehavin,是的
- 8楼幻天芒
- 哇靠,现在项目都这么值钱?话说,仅仅从此文还没看到该架构的精髓。
- Re: 丰叔叔
- @幻天芒,只是简单的写了点身份验证和权限管理。
- 7楼小菜?大神?
- 这也上首页了,没有好文章了
- Re: 丰叔叔
- @小菜?大神?,(┬_┬),文采不够
- 6楼隋裔
- 问下,这个大单子多大?
- Re: 丰叔叔
- @隋裔,10几万的项目吧
- 5楼朱妖
- 我就看看
- 4楼游戏人间
- 期待项目的介绍。
- 3楼Honedream
- 别的不提了,自己弄了一套非常简单的User Role系统,,结果还很糟糕,连一人多角色都不行,,这轮子重复发明的真没必要,直接用ASP.NET Identity不挺好么,省事还更成熟。
- Re: 丰叔叔
- @Honedream,ASP.NET Identity里面有太多多余的东西了
- Re: sun8134
- @Honedream,引用别的不提了,自己弄了一套非常简单的User Role系统,,结果还很糟糕,连一人多角色都不行,,这轮子重复发明的真没必要,直接用ASP.NET Identity不挺好么,省事还更成熟。,Identity下更新Role貌似要注销后重登录才行,感觉不大方便
- 2楼学亮
- 直接在控制器中调用EF好吗?
- Re: 丰叔叔
- @学亮,为了方便,没必要按照三层弄个DAL。
- 1楼蔡大卫
- Model,非Modle