Asp.Net Core 項目實戰之權限管理系統(5) 用戶登陸

0 Asp.Net Core 項目實戰之權限管理系統(0) 無中生有css

1 Asp.Net Core 項目實戰之權限管理系統(1) 使用AdminLTE搭建前端html

2 Asp.Net Core 項目實戰之權限管理系統(2) 功能及實體設計前端

3 Asp.Net Core 項目實戰之權限管理系統(3) 經過EntityFramework Core使用PostgreSQLjquery

4 Asp.Net Core 項目實戰之權限管理系統(4) 依賴注入、倉儲、服務的多項目分層實現git

5 Asp.Net Core 項目實戰之權限管理系統(5) 用戶登陸github

6 Asp.Net Core 項目實戰之權限管理系統(6) 功能管理sql

7 Asp.Net Core 項目實戰之權限管理系統(7) 組織機構、角色、用戶權限數據庫

8 Asp.Net Core 項目實戰之權限管理系統(8) 功能菜單的動態加載json

github源碼地址bootstrap

0 TagHelper的使用

TagHelper是Asp.Net Core中提供的全新的服務端代碼參與建立和渲染 HTML 元素的方法,TagHelpers 在 Razor視圖中減小或避免了 HTML 和 C# 之間的顯示轉換,它具備如下特色:

  • 一種友好的Html開發體驗

   Razor 標記使用 Tag Helpers 看起來更像標準的 HTML。熟悉 HTML/CSS/JavaScript 的前端設計師在沒有學習 C# Razor 語法的狀況下可以編輯 Razor 。

  • 提供豐富的智能感知環境來建立 HTML和Razor標記

   經過Microsoft.AspNetCore.Razor.Tools提供智能感知和智能提醒,大大提升編碼效率。

  • 提供服務器端更強大,更可靠和可維護代碼的html渲染方式

   TagHelper的使用通常放在「視圖導入頁」中,視圖導入頁中還會放置咱們會用到的服務端引用。

0.0 建立視圖導入頁

在Fonour.MVC項目中,右鍵Views文件夾,添加新項,選擇MVC視圖導入頁,添加一個默認名稱爲 「_ViewImports.cshtml」的視圖導入頁。

1

修改視圖導入頁的內容以下:

@addTagHelper *, Microsoft.AspNetCore.Mvc.TagHelpers

0.1 使用Microsoft.AspNetCore.Razor.Tools

Microsoft.AspNetCore.Razor.Tools可以提供TagHelper的智能感知提示和代碼加粗高亮顯示。

  • 經過NuGet程序包管理器安裝
  • 經過NuGet程序包控制檯安裝     
  • 直接修改project.json

最終在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提示。

1

一個完整的使用TagHelper的label標籤建立完成後,會以加粗高亮的方式顯示。你本身能夠根據喜愛在 工具 > 選項 > 環境 > 字體和顏色 中自定義TagHelperElement的字體和顏色。更多關於TagHelper的使用,能夠找專門的文章進行了解。

注意:若是安裝完Microsoft.AspNetCore.Razor.Tools時不會出現智能提示,重啓一下Visual Studio便可。

1 Asp.Net Core中Session中間件的使用

咱們須要在用戶登陸之後記錄當前登陸用戶的會話狀態,ASP.NET Core 發佈了一個關於會話的程序包,裏面提供了用於管理會話狀態的中間件。你能夠在 project.json 中加入對Microsoft.AspNetCore.Session的引用來安裝這個程序包。

1.0 Microsoft.AspNetCore.Session使用

0 Microsoft.AspNetCore.Session依賴項添加

經過NuGet程序包管理器、控制檯、或直接修改project.json文件,添加對Microsoft.AspNetCore.Session中間件的引用。

1

1 修改Startup.cs文件的的ConfigureServices方法,增長Session服務註冊

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); //初始化數據
}

1.1 Byte數組與對象轉換幫助類ByteConvertHelper

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);
    }
}

2 用戶登陸實現

有了以上準備工做,咱們能夠開始正式實現用戶登陸功能了。

2.0 Model

在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

2.1 View

使用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>

關鍵說明

  • @model LoginModel  指定頁面綁定的模型爲LoginModel。
  • asp-controller 指定form標籤提交時對應的控制器名稱。
  • asp-action 指定form標籤提交時對應的Action名稱。
  • asp-for 指定HTML元素綁定的模型對應的屬性名稱。
  • asp-validation-for 綁定對應模型屬性名稱,模型驗證失敗時,顯示模型定義的ErrorMessage。
  • asp-validation-summary="All" 顯示全部驗證失敗的錯誤信息。

2.2 Controller

修改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);
}

到此咱們基本上就完成了用戶登陸的邏輯,以及服務器端的登陸驗證。當輸入用戶名、密碼信息不符合條件時,會給出詳細的錯誤提示。

1

2

2.3 驗證信息美化、記住我功能的實現

上面咱們只作了服務端驗證,並且錯誤信息的提示並非很美觀,咱們能夠根據須要修改樣式對錯誤提示信息進行美化。我這裏直接使用Layer.js這個前端庫。

驗證信息美化

修改用戶登陸控制器方法,有錯誤信息時只返回第一條錯誤信息,客戶端自行處理錯誤信息。

[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>

此時運行程序,服務器端錯誤信息的展現樣式已經比較美觀了。

1

記住我

記住我,主要是記錄輸入的用戶名及密碼,經過cookie保存,首先經過Bower前端包管理器增長Jquery.Cookie的引用。在Login/Index.cshtml中增長對jquery.cookie.js的引用。

<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>

輸入用戶名密碼登錄後,再次回到登陸界面,會發現用戶名及密碼已經填充。

3 用戶是否登陸控制器攔截

一個最基本的控制器攔截,就是當咱們直接經過在地址欄輸入訪問路由地址時,首先應該判斷用戶是否已經登陸,若是沒有登陸應該實現跳轉到登陸頁面。大體思路是經過重寫Controller的OnActionExecuting方法,在OnActionExecuting方法中判斷用戶是否登陸並實現跳轉。

3.1 新建控制器基類

在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,會發現已經自動跳轉到系統登陸界面。

4 總結

本次主要介紹了TagHelper的簡單實用;Asp.Net Core中的Session中間件的使用;以及系統登陸的服務端驗證,並對控制器的訪問進行了統一的是否登陸驗證攔截。下一節主要進行功能管理的實現。

相關文章
相關標籤/搜索