.Net Core3.1 MVC + EF Core+ AutoFac+LayUI+Sqlserver的框架搭建--------登录
大家都应该知道用户登录要保存cookies信息值,在core里还要注册的。首先封装一下cookies:
HttpContextHelper:
using Microsoft.AspNetCore.Http; using System; using System.Collections.Generic; using System.Text; namespace Core.Net.Common.Core.Net.Core.Cookies { public class HttpContextHelper { public static IHttpContextAccessor Accessor; public static HttpContext GetContext() { return Accessor.HttpContext; } } }
UserModel:
using System; using System.Collections.Generic; using System.Text; namespace Core.Net.Common.Core.Net.Core.Cookies { public class UserModel { public int UserId { get; set; } public string UserName { get; set; } public int DeaprtId { get; set; } public int RoleId { get; set; } public string JiGuang { get; set; } public string Email { get; set; } public int IsUse { get; set; } public DateTime LoginTime { get; set; } } }
UserHelper:
using Core.Net.Common.Core.Net.Core.Security; using Microsoft.AspNetCore.Authentication; using Microsoft.AspNetCore.Authentication.Cookies; using System; using System.Collections.Generic; using System.Security.Claims; using System.Text; namespace Core.Net.Common.Core.Net.Core.Cookies { /// <summary> /// 存储用户登录信息,登录过期时间为2个小时,具体配置可以到Startup里面自行修改 /// </summary> public class UserHelper { public static string cookieName = "ProjectOperatorUser"; private static string LoginAESKey = "k9ZhPiDyKZ6wNv36rsRUbRLBFYLSJ5br"; //AES.CreateAesKey().Key 可获得 /// <summary> /// 传入整个用户登录信息 /// </summary> /// <param name="model"></param> /// <returns></returns> public static bool insert(UserModel model) { var claims = new List<Claim>(); claims.Add(new Claim(ClaimTypes.Name, AES.AESEncrypt(model.ToJson(),LoginAESKey))); var indentity = new ClaimsIdentity(claims,cookieName); var principal = new ClaimsPrincipal(indentity); HttpContextHelper.GetContext().SignInAsync(CookieAuthenticationDefaults.AuthenticationScheme, principal); //验证是否授权成功 if (principal.Identity.IsAuthenticated) { return true; } else { return false; } } /// <summary> /// 获取用户角色 /// </summary> /// <returns></returns> public static int GetRoleId() { return GetCurrent().RoleId; } /// <summary> /// 获取用户Id /// </summary> /// <returns></returns> public static int GetUserId() { return GetCurrent().UserId; } /// <summary> /// 获取用户名 /// </summary> /// <returns></returns> public static string GetUserName() { return GetCurrent().UserName; } /// <summary> /// 获取部门Id /// </summary> /// <returns></returns> public static int GetDepart() { return GetCurrent().DeaprtId; } /// <summary> /// 获取整个用户信息 /// </summary> /// <returns></returns> public static UserModel GetCurrent() { string userinfo = HttpContextHelper.GetContext().User.Identity.Name; if (string.IsNullOrEmpty(userinfo)) { return null; } else { return AES.AESDecrypt(userinfo, LoginAESKey).ToObject<UserModel>(); } } } }
这里cookies信息用了AES加密:
AES:
using System; using System.Collections.Generic; using System.IO; using System.Security.Cryptography; using System.Text; namespace Core.Net.Common.Core.Net.Core.Security { /// <summary> /// AES加密、解密帮助类 /// </summary> public class AES { /// <summary> /// AES encrypt ( no IV) /// </summary> /// <param name="data">Raw data</param> /// <param name="key">Key, requires 32 bits</param> /// <returns>Encrypted string</returns> public static string AESEncrypt(string data, string key) { Check.Argument.IsNotEmpty(data, nameof(data)); Check.Argument.IsNotEmpty(key, nameof(key)); Check.Argument.IsNotOutOfRange(key.Length, 32, 32, nameof(key)); using (MemoryStream Memory = new MemoryStream()) { using (Aes aes = Aes.Create()) { byte[] plainBytes = Encoding.UTF8.GetBytes(data); Byte[] bKey = new Byte[32]; Array.Copy(Encoding.UTF8.GetBytes(key.PadRight(bKey.Length)), bKey, bKey.Length); aes.Mode = CipherMode.ECB; aes.Padding = PaddingMode.PKCS7; aes.KeySize = 128; //aes.Key = _key; aes.Key = bKey; //aes.IV = _iV; using (CryptoStream cryptoStream = new CryptoStream(Memory, aes.CreateEncryptor(), CryptoStreamMode.Write)) { try { cryptoStream.Write(plainBytes, 0, plainBytes.Length); cryptoStream.FlushFinalBlock(); return Convert.ToBase64String(Memory.ToArray()); } catch (Exception ex) { return null; } } } } } /// <summary> /// AES decrypt( no IV) /// </summary> /// <param name="data">Encrypted data</param> /// <param name="key">Key, requires 32 bits</param> /// <returns>Decrypted string</returns> public static string AESDecrypt(string data, string key) { Check.Argument.IsNotEmpty(data, nameof(data)); Check.Argument.IsNotEmpty(key, nameof(key)); Check.Argument.IsNotOutOfRange(key.Length, 32, 32, nameof(key)); Byte[] encryptedBytes = Convert.FromBase64String(data); Byte[] bKey = new Byte[32]; Array.Copy(Encoding.UTF8.GetBytes(key.PadRight(bKey.Length)), bKey, bKey.Length); using (MemoryStream Memory = new MemoryStream(encryptedBytes)) { using (Aes aes = Aes.Create()) { aes.Mode = CipherMode.ECB; aes.Padding = PaddingMode.PKCS7; aes.KeySize = 128; aes.Key = bKey; //aes.IV = _iV; using (CryptoStream cryptoStream = new CryptoStream(Memory, aes.CreateDecryptor(), CryptoStreamMode.Read)) { try { byte[] tmp = new byte[encryptedBytes.Length]; int len = cryptoStream.Read(tmp, 0, encryptedBytes.Length); byte[] ret = new byte[len]; Array.Copy(tmp, 0, ret, 0, len); return Encoding.UTF8.GetString(ret); } catch (Exception ex) { return null; } } } } } private static string GetRandomStr(int length) { char[] arrChar = new char[]{ 'a','b','d','c','e','f','g','h','i','j','k','l','m','n','p','r','q','s','t','u','v','w','z','y','x', '0','1','2','3','4','5','6','7','8','9', 'A','B','C','D','E','F','G','H','I','J','K','L','M','N','Q','P','R','T','S','V','U','W','X','Y','Z' }; StringBuilder num = new StringBuilder(); Random rnd = new Random(DateTime.Now.Millisecond); for (int i = 0; i < length; i++) { num.Append(arrChar[rnd.Next(0, arrChar.Length)].ToString()); } return num.ToString(); } public class AESKey { /// <summary> /// ase key /// </summary> public string Key { get; set; } /// <summary> /// ase IV /// </summary> public string IV { get; set; } } /// <summary> /// Create ase key /// </summary> /// <returns></returns> public static AESKey CreateAesKey() { return new AESKey() { Key = GetRandomStr(32), IV = GetRandomStr(16) }; } } }
Check:
using System; using System.Collections.Generic; using System.IO; using System.Text; namespace Core.Net.Common.Core.Net.Core.Security { public class Check { internal Check() { } public class Argument { internal Argument() { } public static void IsNotEmpty(Guid argument, string argumentName) { if (argument == Guid.Empty) { throw new ArgumentException(string.Format("\"{0}\" 不能为空Guid.", argumentName), argumentName); } } public static void IsNotEmpty(string argument, string argumentName) { if (string.IsNullOrEmpty((argument ?? string.Empty).Trim())) { throw new ArgumentException(string.Format("\"{0}\" 不能为空.", argumentName), argumentName); } } public static void IsNotOutOfLength(string argument, int length, string argumentName) { if (argument.Trim().Length > length) { throw new ArgumentException(string.Format("\"{0}\" 不能超过 {1} 字符.", argumentName, length), argumentName); } } public static void IsNotNull(object argument, string argumentName, string message = "") { if (argument == null) { throw new ArgumentNullException(argumentName, message); } } public static void IsNotNegative(int argument, string argumentName) { if (argument < 0) { throw new ArgumentOutOfRangeException(argumentName); } } public static void IsNotNegativeOrZero(int argument, string argumentName) { if (argument <= 0) { throw new ArgumentOutOfRangeException(argumentName); } } public static void IsNotNegative(long argument, string argumentName) { if (argument < 0) { throw new ArgumentOutOfRangeException(argumentName); } } public static void IsNotNegativeOrZero(long argument, string argumentName) { if (argument <= 0) { throw new ArgumentOutOfRangeException(argumentName); } } public static void IsNotNegative(float argument, string argumentName) { if (argument < 0) { throw new ArgumentOutOfRangeException(argumentName); } } public static void IsNotNegativeOrZero(float argument, string argumentName) { if (argument <= 0) { throw new ArgumentOutOfRangeException(argumentName); } } public static void IsNotNegative(decimal argument, string argumentName) { if (argument < 0) { throw new ArgumentOutOfRangeException(argumentName); } } public static void IsNotNegativeOrZero(decimal argument, string argumentName) { if (argument <= 0) { throw new ArgumentOutOfRangeException(argumentName); } } public static void IsNotInvalidDate(DateTime argument, string argumentName) { DateTime MinDate = new DateTime(1900, 1, 1); DateTime MaxDate = new DateTime(9999, 12, 31, 23, 59, 59, 999); if (!((argument >= MinDate) && (argument <= MaxDate))) { throw new ArgumentOutOfRangeException(argumentName); } } public static void IsNotInPast(DateTime argument, string argumentName) { if (argument < DateTime.Now) { throw new ArgumentOutOfRangeException(argumentName); } } public static void IsNotInFuture(DateTime argument, string argumentName) { if (argument > DateTime.Now) { throw new ArgumentOutOfRangeException(argumentName); } } public static void IsNotNegative(TimeSpan argument, string argumentName) { if (argument < TimeSpan.Zero) { throw new ArgumentOutOfRangeException(argumentName); } } public static void IsNotNegativeOrZero(TimeSpan argument, string argumentName) { if (argument <= TimeSpan.Zero) { throw new ArgumentOutOfRangeException(argumentName); } } public static void IsNotEmpty<T>(ICollection<T> argument, string argumentName) { IsNotNull(argument, argumentName, "集合不能为Null"); if (argument.Count == 0) { throw new ArgumentException("集合不能为空.", argumentName); } } public static void IsNotOutOfRange(int argument, int min, int max, string argumentName) { if ((argument < min) || (argument > max)) { throw new ArgumentOutOfRangeException(argumentName, string.Format("{0} 必须在此区间 \"{1}\"-\"{2}\".", argumentName, min, max)); } } public static void IsNotExistsFile(string argument, string argumentName) { IsNotEmpty(argument, argumentName); if (!File.Exists(argument)) { throw new ArgumentException(string.Format("\"{0}\" 文件不存在", argumentName), argumentName); } } public static void IsNotExistsDirectory(string argument, string argumentName) { IsNotEmpty(argument, argumentName); if (!Directory.Exists(argument)) { throw new ArgumentException(string.Format("\"{0}\" 目录不存在", argumentName), argumentName); } } } } }
MD5加密:
using System; using System.Collections.Generic; using System.Security.Cryptography; using System.Text; namespace Core.Net.Common.Core.Net.Core.Security { /// <summary> /// MD5加密 /// </summary> public class Md5 { /// <summary> /// MD5加密,保留32位大写 /// </summary> /// <param name="str">加密字符</param> /// <returns></returns> public static string Md5Upper32(string str) { return md5(str, 32).ToUpper(); } /// <summary> /// MD5加密 /// </summary> /// <param name="str">加密字符</param> /// <param name="code">加密位数16/32</param> /// <returns></returns> public static string md5(string str, int code) { using (var md5 = MD5.Create()) { var result = md5.ComputeHash(Encoding.ASCII.GetBytes(str)); var strResult = BitConverter.ToString(result); return strResult.Replace("-", ""); } } } }
新建一个登录控制器:
LoginController.cs:
using Core.Net.Common.Core.Net.Core; using Core.Net.Common.Core.Net.Core.Cookies; using Core.Net.Common.Core.Net.Core.Security; using Core.Net.IServices; using Core.Net.Model.System; using Core.Net.Web.Common; using Microsoft.AspNetCore.Authentication; using Microsoft.AspNetCore.Authentication.Cookies; using Microsoft.AspNetCore.Http; using Microsoft.AspNetCore.Mvc; using System; using System.Collections.Generic; using System.Drawing.Imaging; using System.IO; using System.Linq; using System.Security.Claims; using System.Threading.Tasks; namespace Core.Net.Web.Areas.SystemManager.Controllers { [Area("SystemManager")] public class LoginController : BaseController { private readonly ICoreNetSystemUserServices _coreNetSystemUserServices; private readonly ICoreNetSystemUserLogServices _coreNetSystemUserLogServices; public LoginController(ICoreNetSystemUserServices coreNetSystemUserServices, ICoreNetSystemUserLogServices coreNetSystemUserLogServices) { _coreNetSystemUserServices = coreNetSystemUserServices; _coreNetSystemUserLogServices = coreNetSystemUserLogServices; } public IActionResult Index() { return View(); } public IActionResult MainPage() { return View(); } [HttpPost] public IActionResult LoginForm() { string LoginCode = HttpContext.Session.GetString("LoginValidateCode"); string CurrentCode = ConvertHelper.SafeParse(Request.Form["VerificationCode"], ""); if (LoginCode != null) { if (LoginCode.Trim().ToLower() != CurrentCode.Trim().ToLower()) { return OperateResult(0, "", "验证错误!", ""); } } else { return OperateResult(0, "", "验证码已经过期!", ""); } string username = ConvertHelper.SafeParse(Request.Form["username"], ""); string password = ConvertHelper.SafeParse(Request.Form["password"], ""); CoreNetSystemUser user = _coreNetSystemUserServices.GetEntityOne(p => p.UserName == username ); password = Md5.Md5Upper32(password); if (user != null && password == user.Passward && user.IsUse == 1) { UserModel userModel = new UserModel(); userModel.UserId = user.UserId; userModel.UserName = user.UserName; userModel.LoginTime = DateTime.Now; userModel.DeaprtId = user.DepartId; userModel.RoleId = user.RoleId; userModel.IsUse = user.IsUse; userModel.JiGuang = user.PushId; userModel.Email = user.Email; //登陆授权 UserHelper.insert(userModel); //添加系统登录日志 CoreNetSystemUserLog LogModel = new CoreNetSystemUserLog(); LogModel.UserId = user.UserId; LogModel.UserName = user.UserName; LogModel.RealName = user.RealName; LogModel.LoginTime = DateTime.Now; LogModel.LoginType = 1; LogModel.UserIP =""; _coreNetSystemUserLogServices.Insert(LogModel); return OperateResult(1, "登陆成功", "", "/systemmanager/Login/MainPage"); } else { return OperateResult(0, "", "账号密码有误", ""); } } /// <summary> /// 混合验证码 /// </summary> /// <returns></returns> public FileContentResult MixVerifyCode() { string code = VerifyCodeHelper.GetSingleObj().CreateVerifyCode(VerifyCodeHelper.VerifyCodeType.MixVerifyCode); var bitmap = VerifyCodeHelper.GetSingleObj().CreateBitmapByImgVerifyCode(code, 100, 40); MemoryStream stream = new MemoryStream(); bitmap.Save(stream, ImageFormat.Gif); HttpContext.Session.SetString("LoginValidateCode", code); return File(stream.ToArray(), "image/gif"); } /// <summary> /// 退出登录 /// </summary> /// <returns></returns> public ActionResult LogOut() { HttpContext.SignOutAsync(CookieAuthenticationDefaults.AuthenticationScheme); return Content("1"); } } }
Index.cshtml:
<!doctype html> <html class="x-admin-sm"> <head> <meta charset="UTF-8"> <title>秦歌项目管理系统2.0</title> <meta name="renderer" content="webkit|ie-comp|ie-stand"> <meta http-equiv="X-UA-Compatible" content="IE=edge,chrome=1"> <meta name="viewport" content="width=device-width,user-scalable=yes, minimum-scale=0.4, initial-scale=0.8" /> <meta http-equiv="Cache-Control" content="no-siteapp" /> <script type="text/javascript" src="https://cdn.bootcss.com/jquery/3.2.1/jquery.min.js"></script> <link rel="stylesheet" href="/xadmin/css/font.css"> <link rel="stylesheet" href="/xadmin/css/login.css"> <link rel="stylesheet" href="/xadmin/css/xadmin.css"> <script src="/xadmin/lib/layui/layui.js" charset="utf-8"></script> <script type="text/javascript" src="/xadmin/js/xadmin.js"></script> <script src="~/xadmin/js/jquery.min.js"></script> <!--[if lt IE 9]> <script src="https://cdn.staticfile.org/html5shiv/r29/html5.min.js"></script> <script src="https://cdn.staticfile.org/respond.js/1.4.2/respond.min.js"></script> <![endif]--> </head> <body class="login-bg"> <div class="login layui-anim layui-anim-up"> <div class="message">标题</div> <div id="darkbannerwrap"></div> <form method="post" class="layui-form" action="/SystemManager/Login/LoginForm"> <input name="username" placeholder="用户名" type="text" lay-verify="required" class="layui-input"> <hr class="hr15"> <input name="password" lay-verify="required" placeholder="密码" autocomplete="off" type="password" class="layui-input"> <hr class="hr15"> @*<img id="nubImg" title="数字验证码" src="/SystemManage/Manage/NumberVerifyCode?random=" alt="vcode" onclick="refresh()" style="cursor:pointer;" /> <img id="abcImg" title="字母验证码" src="/SystemManage/Manage/AbcVerifyCode" alt="vcode" onclick="this.src=this.src+'?'" style="cursor:pointer;" />*@ <div class="layui-input-inline layui-show-xs-block"> <input name="VerificationCode" lay-verify="required" placeholder="验证码" type="text" class="layui-input"> </div> <div class="layui-input-inline layui-show-xs-block"> <img id="mixImg" title="数字字母混合验证码" src="/SystemManager/Login/MixVerifyCode" alt="vcode" onclick="this.src=this.src+'?'" style="cursor:pointer;" /> </div> <hr class="hr15"> <input value="登录" lay-submit lay-filter="login" style="width:100%;" type="submit"> <hr class="hr20"> </form> </div> <script> $(function () { layui.use('form', function () { var form = layui.form; //监听提交 form.on('submit(login)', function (data) { $.ajax({ url: data.form.action, data: data.field, type: "post", success: function (res) { var returnres = $.parseJSON(res); layer.msg(returnres.msg, { icon: 1, time: 1000, end: function () { if (returnres.status > 0 && returnres.url.length > 0) { window.parent.location.href = returnres.url; } } }); } }); return false; }); }); })</script> </body> </html>
最后在startup里面注册cookies:
using Autofac; using Core.Net.Common.Core.Net.Core.Cookies; using Core.Net.Common.Core.Net.Core.Nlog4; using Core.Net.Common.Core.Net.Core.QuartZ.JobRun; using Core.Net.Common.Core.Net.Core.Redis; using Core.Net.Common.Core.Net.Data; using Core.Net.Common.Core.Net.Data.DBSql; using Core.Net.Web.Common.VerificationCode; using Microsoft.AspNetCore.Authentication.Cookies; using Microsoft.AspNetCore.Builder; using Microsoft.AspNetCore.Hosting; using Microsoft.AspNetCore.Http; using Microsoft.AspNetCore.Mvc; using Microsoft.AspNetCore.Mvc.Controllers; using Microsoft.EntityFrameworkCore; using Microsoft.Extensions.Configuration; using Microsoft.Extensions.DependencyInjection; using Microsoft.Extensions.DependencyInjection.Extensions; using Microsoft.Extensions.FileProviders; using Microsoft.Extensions.Hosting; using Microsoft.Extensions.Logging; using System; using System.IO; using System.Linq; namespace Core.Net.Web { public class Startup { public Startup(IConfiguration configuration) { Configuration = configuration; } public IConfiguration Configuration { get; } // This method gets called by the runtime. Use this method to add services to the container. public void ConfigureServices(IServiceCollection services) { services.AddControllersWithViews(); ////2、使用HttpContext需要注入 services.AddDistributedMemoryCache();//启用session之前必须先添加内存 // //services.AddSession(); services.AddSession(options => { options.Cookie.Name = ".AdventureWorks.Session"; options.IdleTimeout = TimeSpan.FromSeconds(2000);//设置session的过期时间 options.Cookie.HttpOnly = true;//设置在浏览器不能通过js获得该cookie的值 }); //配置authorrize services.AddAuthentication(b => { b.DefaultAuthenticateScheme = CookieAuthenticationDefaults.AuthenticationScheme; b.DefaultChallengeScheme = CookieAuthenticationDefaults.AuthenticationScheme; b.DefaultSignInScheme = CookieAuthenticationDefaults.AuthenticationScheme; }).AddCookie(b => { //登陆地址 b.LoginPath = "/login"; //sid b.Cookie.Name = "My_SessionId"; // b.Cookie.Domain = "shenniu.core.com"; b.Cookie.Path = "/"; b.Cookie.HttpOnly = true; //b.Cookie.Expiration = new TimeSpan(0, 0, 30); b.ExpireTimeSpan = new TimeSpan(2, 0, 0); }); services.AddSingleton<IHttpContextAccessor, HttpContextAccessor>(); services.AddSingleton<IQRCode,RaffQRCode>();//验证码注入 services.AddMvc().AddRazorRuntimeCompilation();//注入修改页面时候,保存可以直接刷新 } // This method gets called by the runtime. Use this method to configure the HTTP request pipeline. [Obsolete] public void Configure(IApplicationBuilder app, IWebHostEnvironment env, IHttpContextAccessor accessor, Microsoft.AspNetCore.Hosting.IApplicationLifetime lifetime, ILoggerFactory loggerFactory) { //注入当前HttpContext HttpContextHelper.Accessor = accessor; if (env.IsDevelopment()) { app.UseDeveloperExceptionPage(); } else { app.UseExceptionHandler("/Home/Error"); // The default HSTS value is 30 days. You may want to change this for production scenarios, see https://aka.ms/aspnetcore-hsts. app.UseHsts(); } app.UseSession(); app.UseHttpsRedirection(); app.UseStaticFiles(); app.UseStaticFiles(new StaticFileOptions() { FileProvider = new PhysicalFileProvider(Path.Combine(Directory.GetCurrentDirectory(), @"UpFile")), RequestPath = new PathString("/UpFile"), ServeUnknownFileTypes = true //允许下载APK,用下面的方式必须把所有JSON都放进去 //ContentTypeProvider = new FileExtensionContentTypeProvider(new Dictionary<string, string>{ // { ".apk","application/vnd.android.package-archive"},{ ".nupkg","application/zip"} //}) }); app.UseCookiePolicy(); app.UseRouting(); app.UseAuthentication(); app.UseAuthorization(); app.UseEndpoints(endpoints => { endpoints.MapControllerRoute( name: "MyArea", pattern: "{area:exists}/{controller=Home}/{action=Index}/{id?}"); endpoints.MapControllerRoute( name: "default", pattern: "{area=SystemManager}/{controller=Login}/{action=Index}/{id?}"); }); } } }
到这里登录功能已经完成:
下篇就介绍一下,AutoFac依赖注入。
.Net Core