0 Asp.Net Core 項目實戰之權限管理系統(0) 無中生有
1 Asp.Net Core 項目實戰之權限管理系統(1) 使用AdminLTE搭建前端
2 Asp.Net Core 項目實戰之權限管理系統(2) 功能及實體設計
3 Asp.Net Core 項目實戰之權限管理系統(3) 通過EntityFramework Core使用PostgreSQL
4 Asp.Net Core 項目實戰之權限管理系統(4) 依賴注入、倉儲、服務的多項目分層實現
5 Asp.Net Core 項目實戰之權限管理系統(5) 用戶登錄
6 Asp.Net Core 項目實戰之權限管理系統(6) 功能管理
github源碼地址
TagHelper是Asp.Net Core中提供的全新的服務端代碼參與創建和渲染 HTML 元素的方法,TagHelpers 在 Razor視圖中減少或避免了 HTML 和 C# 之間的顯示轉換,它具有以下特點:
Razor 標記使用 Tag Helpers 看起來更像標准的 HTML。熟悉 HTML/CSS/JavaScript 的前端設計師在沒有學習 C# Razor 語法的情況下能夠編輯 Razor 。
通過Microsoft.AspNetCore.Razor.Tools提供智能感知和智能提醒,大大提高編碼效率。
TagHelper的使用一般放在“視圖導入頁”中,視圖導入頁中還會放置我們會用到的服務端引用。
在Fonour.MVC項目中,右鍵Views文件夾,添加新項,選擇MVC視圖導入頁,添加一個默認名稱為 “_ViewImports.cshtml”的視圖導入頁。
@addTagHelper *, Microsoft.AspNetCore.Mvc.TagHelpers
Microsoft.AspNetCore.Razor.Tools能夠提供TagHelper的智能感知提示和代碼加粗高亮顯示。
最終在project.json文件中的dependencies及tools配置節中會出現Microsoft.AspNetCore.Razor.Tools
"dependencies": { "Microsoft.NETCore.App": "1.0.1", "Microsoft.AspNetCore.Diagnostics": "1.0.0", "Microsoft.AspNetCore.Server.IISIntegration": "1.0.0", "Microsoft.AspNetCore.Server.Kestrel": "1.0.1", "Microsoft.Extensions.Logging.Console": "1.0.0", "Microsoft.AspNetCore.Mvc": "1.0.1", "Microsoft.AspNetCore.StaticFiles": "1.0.0", "Microsoft.Extensions.Configuration": "1.0.0", "Microsoft.Extensions.Configuration.FileExtensions": "1.0.0", "Microsoft.Extensions.Configuration.Json": "1.0.0", "Fonour.Application": "1.0.0-*", "Microsoft.AspNetCore.Razor.Tools": "1.0.0-preview2-final", "Microsoft.AspNetCore.Session": "1.0.0", "Fonour.Utility": "1.0.0-*" }, "tools": { "Microsoft.AspNetCore.Server.IISIntegration.Tools": "1.0.0-preview2-final", "Microsoft.AspNetCore.Razor.Tools": "1.0.0-preview2-final" },
打開Login/Index.cshtml文件,隨意輸入一個label標簽,發現已經可以出現asp-for的TagHelper提示。
public void ConfigureServices(IServiceCollection services) { //獲取數據庫連接字符串 var sqlConnectionString = Configuration.GetConnectionString("Default"); //添加數據上下文 services.AddDbContext<FonourDbContext>(options =>options.UseNpgsql(sqlConnectionString)); services.AddScoped<IUserRepository, UserRepository>(); services.AddScoped<IUserAppService, UserAppService>(); services.AddMvc(); //Session服務 services.AddSession(); }
2 修改Startup.cs文件的的Configure方法,請求管道中啟用Session
public void Configure(IApplicationBuilder app, IHostingEnvironment env, ILoggerFactory loggerFactory) { loggerFactory.AddConsole(); if (env.IsDevelopment()) { //開發環境異常處理 app.UseDeveloperExceptionPage(); } else { //生產環境異常處理 app.UseExceptionHandler("/Shared/Error"); } //使用靜態文件 app.UseStaticFiles(); //Session app.UseSession(); //使用Mvc,設置默認路由為系統登錄 app.UseMvc(routes => { routes.MapRoute( name: "default", template: "{controller=Login}/{action=Index}/{id?}"); }); SeedData.Initialize(app.ApplicationServices); //初始化數據 }
Asp.Net Core中Session的Set方法必須提供一個Byte[]數組來存放對象,此外為了方便使用,還提供了SetString和SetInt32擴展方法。
void Set(string key, byte[] value);
我們在Fonour.Utility項目中增加一個Byte數組與對象互相轉換的幫助類ByteConVertHelper。
public class ByteConvertHelper { /// <summary> /// 將對象轉換為byte數組 /// </summary> /// <param name="obj">被轉換對象</param> /// <returns>轉換後byte數組</returns> public static byte[] Object2Bytes(object obj) { string json = JsonConvert.SerializeObject(obj); byte[] serializedResult = System.Text.Encoding.UTF8.GetBytes(json); return serializedResult; } /// <summary> /// 將byte數組轉換成對象 /// </summary> /// <param name="buff">被轉換byte數組</param> /// <returns>轉換完成後的對象</returns> public static object Bytes2Object(byte[] buff) { string json = System.Text.Encoding.UTF8.GetString(buff); return JsonConvert.DeserializeObject<object>(json); } /// <summary> /// 將byte數組轉換成對象 /// </summary> /// <param name="buff">被轉換byte數組</param> /// <returns>轉換完成後的對象</returns> public static T Bytes2Object<T>(byte[] buff) { string json = System.Text.Encoding.UTF8.GetString(buff); return JsonConvert.DeserializeObject<T>(json); } }
有了以上准備工作,我們可以開始正式實現用戶登錄功能了。
在Fonour.MVC項目中增加一個Models文件夾,用於存放前端交互使用的Model類,在Models文件夾下新建一個LoginModel類,並通過DataAnnotations特性指定屬性的驗證信息。
public class LoginModel { [Required(ErrorMessage = "用戶名不能為空。")] public string UserName { get; set; } [Required(ErrorMessage = "密碼不能為空。")] [DataType(DataType.Password)] public string Password { get; set; } public bool RememberMe { get; set; } }
在視圖導入頁_ViewImport.cshtml中增加對模型的引用
@using Fonour.MVC.Models
使用TagHelper修改Login/Index.cshtml為如下內容
@{ Layout = null; } @model LoginModel <!DOCTYPE html> <html> <head> <title>系統登錄</title> <!-- Tell the browser to be responsive to screen width --> <meta content="width=device-width, initial-scale=1, maximum-scale=1, user-scalable=no" name="viewport"> <link rel="stylesheet" href="~/lib/bootstrap/dist/css/bootstrap.css"> <link rel="stylesheet" href="~/lib/font-awesome/css/font-awesome.css"> <link rel="stylesheet" href="~/css/AdminLTE.css"> <link rel="stylesheet" href="~/lib/iCheck/skins/square/blue.css"> </head> <body class="hold-transition login-page"> <div class="login-box"> <div class="login-logo"> <a href="http://fonour.cnblogs.com" target="_blank"><b>Fonour</b></a> </div> <!-- /.login-logo --> <div class="login-box-body"> <p class="login-box-msg">權限管理系統</p> <div asp-validation-summary="All" class="text-danger"></div> <form asp-controller="Login" asp-action="Index" method="post"> <div class="form-group has-feedback"> <input asp-for="UserName" type="text" class="form-control" placeholder="用戶名"> <span class="glyphicon glyphicon-user form-control-feedback"></span> <span asp-validation-for="UserName" class="text-danger"></span> </div> <div class="form-group has-feedback"> <input asp-for="Password" type="password" class="form-control" placeholder="密碼"> <span class="glyphicon glyphicon-lock form-control-feedback"></span> </div> <div class="row"> <div class="col-xs-8"> <div class="checkbox icheck"> <label> <input asp-for="RememberMe" type="checkbox"> 記住我 </label> </div> </div> <!-- /.col --> <div class="col-xs-4"> <button type="submit" class="btn btn-primary btn-block btn-flat">登錄</button> </div> <!-- /.col --> </div> </form> </div> <!-- /.login-box-body --> </div> <!-- /.login-box --> <script src="~/lib/jquery/dist/jquery.js"></script> <script src="~/lib/bootstrap/dist/js/bootstrap.js"></script> <script src="~/lib/iCheck/icheck.js"></script> <script> $(function () { $('input').iCheck({ checkboxClass: 'icheckbox_square-blue', radioClass: 'iradio_square-blue', increaseArea: '20%' // optional }); }); </script> </body> </html>
關鍵說明
修改LoginController,增加用戶登錄對應的控制器方法。
[HttpPost] public IActionResult Index(LoginModel model) { if (ModelState.IsValid) { //檢查用戶信息 var user = _userAppService.CheckUser(model.UserName, model.Password); if (user != null) { //記錄Session HttpContext.Session.Set("CurrentUser", ByteConvertHelper.Object2Bytes(user)); //跳轉到系統首頁 return RedirectToAction("Index", "Home"); } ModelState.AddModelError("", "用戶名或密碼錯誤。"); return View(); } return View(model); }
到此我們基本上就完成了用戶登錄的邏輯,以及服務器端的登錄驗證。當輸入用戶名、密碼信息不符合條件時,會給出詳細的錯誤提示。
[HttpPost] public IActionResult Index(LoginModel model) { if (ModelState.IsValid) { //檢查用戶信息 var user = _userAppService.CheckUser(model.UserName, model.Password); if (user != null) { //記錄Session HttpContext.Session.Set("CurrentUser", ByteConvertHelper.Object2Bytes(user)); //跳轉到系統首頁 return RedirectToAction("Index", "Home"); } ViewBag.ErrorInfo = "用戶名或密碼錯誤。"; return View(); } ViewBag.ErrorInfo = ModelState.Values.First().Errors[0].ErrorMessage; return View(model); }
通過Bower程序包管理器添加對Layer的引用,在Login/Index.cshtml中增加對layer.js的引用
<script src="~/lib/layer/layer.js"></script>
增加一個隱藏的input標簽用於記錄錯誤信息。
<input id="errorInfo" type="hidden" value="@ViewBag.ErrorInfo" />
初始化完成後增加對錯誤信息的處理。
<script> $(function () { $('input').iCheck({ checkboxClass: 'icheckbox_square-blue', radioClass: 'iradio_square-blue', increaseArea: '20%' // optional }); //顯示服務端驗證的錯誤信息 if ($("#errorInfo").val()) { layer.tips($("#errorInfo").val(), "#btnLogin"); }; }); </script>
此時運行程序,服務器端錯誤信息的展示樣式已經比較美觀了。
<script src="~/lib/jquery.cookie/src/jquery.cookie.js"></script>
增加form的onsubmit方法
<form asp-controller="Login" asp-action="Index" method="post" onsubmit="onSubmit()">
<script> $(function () { $('input').iCheck({ checkboxClass: 'icheckbox_square-blue', radioClass: 'iradio_square-blue', increaseArea: '20%' // optional }); //顯示服務端驗證的錯誤信息 if ($("#errorInfo").val()) { layer.tips($("#errorInfo").val(), "#btnLogin"); }; //判斷之前是否有設置cookie,如果有,則設置【記住我】選擇框 if ($.cookie("fonour_userName") != undefined) { $("#RememberMe").attr("checked", "checked"); } else { $("#RememberMe").removeAttr("checked"); } //讀取cookie if ($("#RememberMe:checked").length > 0) { $("#UserName").val($.cookie("fonour_userName")); $("#Password").val($.cookie("fonour_password")); } }); //根據是否勾選記住我記錄或清除cookie function onSubmit() { if ($("#RememberMe:checked").length > 0) {//設置cookie $.cookie("fonour_userName", $("#UserName").val()); $.cookie("fonour_password", $("#Password").val()); } else {//清除cookie $.removeCookie("fonour_userName"); $.removeCookie("fonour_password"); } }; </script>
輸入用戶名密碼登陸後,再次回到登錄界面,會發現用戶名及密碼已經填充。
一個最基本的控制器攔截,就是當我們直接通過在地址欄輸入訪問路由地址時,首先應該判斷用戶是否已經登錄,如果沒有登錄應該實現跳轉到登錄頁面。大致思路是通過重寫Controller的OnActionExecuting方法,在OnActionExecuting方法中判斷用戶是否登錄並實現跳轉。
在Fonour.MVC中右鍵Controllers文件夾,添加一個名稱為FonourControllerBase的控制器基類,內容如下。
public abstract class FonourControllerBase : Controller { public override void OnActionExecuting(ActionExecutingContext filterContext) { byte[] result; filterContext.HttpContext.Session.TryGetValue("CurrentUser",out result); if (result == null) { filterContext.Result = new RedirectResult("/Login/Index"); return; } base.OnActionExecuting(filterContext); } }
需要進行登陸驗證的控制器,修改為從FonourControllerBase繼承,這裡我們修改HomeController
public class HomeController : FonourControllerBase
啟動程序,在未登錄情況下,通過地址欄直接訪問/Home/Index,會發現已經自動跳轉到系統登錄界面。
本次主要介紹了TagHelper的簡單實用;Asp.Net Core中的Session中間件的使用;以及系統登錄的服務端驗證,並對控制器的訪問進行了統一的是否登錄驗證攔截。下一節主要進行功能管理的實現。