Asp.Net Core 项目实战之权限管理系统(5) 用户登录
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) 功能管理
7 Asp.Net Core 项目实战之权限管理系统(7) 组织机构、角色、用户权限
8 Asp.Net Core 项目实战之权限管理系统(8) 功能菜单的动态加载
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”的视图导入页。

修改视图导入页的内容如下:
@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提示。

一个完整的使用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 修改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);
}
到此我们基本上就完成了用户登录的逻辑,以及服务器端的登录验证。当输入用户名、密码信息不符合条件时,会给出详细的错误提示。


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[].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>
此时运行程序,服务器端错误信息的展示样式已经比较美观了。

记住我
记住我,主要是记录输入的用户名及密码,通过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中间件的使用;以及系统登录的服务端验证,并对控制器的访问进行了统一的是否登录验证拦截。下一节主要进行功能管理的实现。
Asp.Net Core 项目实战之权限管理系统(5) 用户登录的更多相关文章
- Asp.Net Core 项目实战之权限管理系统(4) 依赖注入、仓储、服务的多项目分层实现
0 Asp.Net Core 项目实战之权限管理系统(0) 无中生有 1 Asp.Net Core 项目实战之权限管理系统(1) 使用AdminLTE搭建前端 2 Asp.Net Core 项目实战之 ...
- Asp.Net Core 项目实战之权限管理系统(0) 无中生有
0 Asp.Net Core 项目实战之权限管理系统(0) 无中生有 1 Asp.Net Core 项目实战之权限管理系统(1) 使用AdminLTE搭建前端 2 Asp.Net Core 项目实战之 ...
- Asp.Net Core 项目实战之权限管理系统(1) 使用AdminLTE搭建前端
0 Asp.Net Core 项目实战之权限管理系统(0) 无中生有 1 Asp.Net Core 项目实战之权限管理系统(1) 使用AdminLTE搭建前端 2 Asp.Net Core 项目实战之 ...
- Asp.Net Core 项目实战之权限管理系统(2) 功能及实体设计
0 Asp.Net Core 项目实战之权限管理系统(0) 无中生有 1 Asp.Net Core 项目实战之权限管理系统(1) 使用AdminLTE搭建前端 2 Asp.Net Core 项目实战之 ...
- Asp.Net Core 项目实战之权限管理系统(3) 通过EntityFramework Core使用PostgreSQL
0 Asp.Net Core 项目实战之权限管理系统(0) 无中生有 1 Asp.Net Core 项目实战之权限管理系统(1) 使用AdminLTE搭建前端 2 Asp.Net Core 项目实战之 ...
- Asp.Net Core 项目实战之权限管理系统(6) 功能管理
0 Asp.Net Core 项目实战之权限管理系统(0) 无中生有 1 Asp.Net Core 项目实战之权限管理系统(1) 使用AdminLTE搭建前端 2 Asp.Net Core 项目实战之 ...
- Asp.Net Core 项目实战之权限管理系统(7) 组织机构、角色、用户权限
0 Asp.Net Core 项目实战之权限管理系统(0) 无中生有 1 Asp.Net Core 项目实战之权限管理系统(1) 使用AdminLTE搭建前端 2 Asp.Net Core 项目实战之 ...
- Asp.Net Core 项目实战之权限管理系统(8) 功能菜单的动态加载
0 Asp.Net Core 项目实战之权限管理系统(0) 无中生有 1 Asp.Net Core 项目实战之权限管理系统(1) 使用AdminLTE搭建前端 2 Asp.Net Core 项目实战之 ...
- Net Core 项目实战之权限管理系统(0)
0 前言 Net Core 项目实战之权限管理系统(0) 无中生有 0 http://www.cnblogs.com/fonour/p/5848933.html 学习的最好方法就是动手去做,这里以 ...
随机推荐
- Hawk 4.4 执行器
执行器是负责将Hawk的结果传送到外部环境的工具.你可以写入数据表,数据库,甚至执行某个特定的动作,或是生成文件等等. 在调试模式下,执行器都是不工作的.这是为了避免产生副作用.否则,每刷新一遍数据, ...
- nginx源码分析之模块初始化
在nginx启动过程中,模块的初始化是整个启动过程中的重要部分,而且了解了模块初始化的过程对应后面具体分析各个模块会有事半功倍的效果.在我看来,分析源码来了解模块的初始化是最直接不过的了,所以下面主要 ...
- AFNetworking 3.0 源码解读 总结(干货)(下)
承接上一篇AFNetworking 3.0 源码解读 总结(干货)(上) 21.网络服务类型NSURLRequestNetworkServiceType 示例代码: typedef NS_ENUM(N ...
- 玩转spring boot——结合redis
一.准备工作 下载redis的windows版zip包:https://github.com/MSOpenTech/redis/releases 运行redis-server.exe程序 出现黑色窗口 ...
- javascript中变量提升的理解
网上找了两个经典的例子 var foo = 1; function bar() { if (!foo) { var foo = 10; } alert(foo); } bar(); // 10 var ...
- BPM配置故事之案例4-子表
公司渐渐对采购管理重视起来了,新招聘了采购主管老李,老李对现有的申请表很不满意,要求将申请物资和申请原因改成物资明细表 物资明细表 小明只好继续致电大毛-- 大毛:把申请物资和申请原因删掉,新增一个数 ...
- Android中的多线程断点下载
首先来看一下多线程下载的原理.多线程下载就是将同一个网络上的原始文件根据线程个数分成均等份,然后每个单独的线程下载对应的一部分,然后再将下载好的文件按照原始文件的顺序"拼接"起来就 ...
- vim环境变量配置、背景色配置
我们使用vi或者vim的时候,如果想要显示行号,可能会这样做:切换到命令模式,然后输入set nu,再按回车键就显示了:还有就是咱们在编写程序的时候,有的时候会希望按下回车键后,光标不是每次都在行首, ...
- [PHP源码阅读]array_push和array_unshift函数
在PHP中,在数组中添加元素也是一种很常用的操作,分别有在数组尾部和头部添加元素,看看PHP内部是如何实现数组插入的操作. 我在github有对PHP源码更详细的注解.感兴趣的可以围观一下,给个sta ...
- [NodeJS] Hello World 起步教程
概述: 做数据,免不了需要展示数据,数据可视化是必须经历的步骤. 本文将提供一个NodeJS的起步教程,是笔者这两天探索的小结. 正文: 1. 为什么使用NodeJS 究竟是以B/S还是C/S的架构 ...