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中間件的使用;以及系統登錄的服務端驗證,並對控制器的訪問進行了統一的是否登錄驗證攔截。下一節主要進行功能管理的實現。