[基于NetCore的简单博客系统]-登录
0-项目背景
一个基于.NET CORE RAZOR PAGES的简单博客系统 技术栈全部采用微软官方实现方式,目的是熟悉新技术
PS:因为是练手,所以UI界面就比较丑咯
1-框架结构(参考ABP框架:因为是练手,所以没有使用ABP基础类,只是参考了他的架子)
1.1)后台用经典的MVC方式实现
1.2)前台打算使用.NetCore RazorPage页面实现
2-表结构
2.1)User - 后台用户
2.2)Member -前台用户
2.3)MemberBlogTagRelationship - 前台用户博文标签表
2.4)Blog -博文
2.5)BlogCategroy -博文分类
2.6)BlogPost- 博文评论
2.7) BlogTag-博文标签(前台用户添加)
2.8)BlogTagRelationship - 博文-博文标签关联表
2.9)添加Entity 基类 参考 ABP 的实体基类,所有实体都是派生自 entity
3-生成数据库(基于EF code-first 模式)
3.1)添加实体与数据库表映射关系
3.2)数据上下文中添加配置
3.3)生成数据库方式参考https://docs.microsoft.com/en-us/ef/core/ 和以前的EF差不多
3.4)SetOneToManyCascadeDeleteConvertion 和 AddEntityTypeConfigruations 都是自定义的扩展方法
1 public static class ModelBuilderExtensions 2 { 3 /// <summary> 4 /// 关掉所有主外键关系的级联删除 5 /// </summary> 6 /// <param name="modelBuilder"></param> 7 /// <param name="deleteBehavior"></param> 8 public static void SetOneToManyCascadeDeleteConvention(this ModelBuilder modelBuilder, DeleteBehavior deleteBehavior = DeleteBehavior.Restrict) 9 { 10 foreach (var relationship in modelBuilder.Model.GetEntityTypes().SelectMany(e => e.GetForeignKeys())) 11 { 12 relationship.DeleteBehavior = deleteBehavior; 13 } 14 } 15 16 /// <summary> 17 /// 自动映射实体与表 18 /// </summary> 19 /// <param name="modelBuilder"></param> 20 /// <param name="executingAssembly"></param> 21 public static void AddEntityTypeConfigurations(this ModelBuilder modelBuilder, Assembly executingAssembly) 22 { 23 var types = executingAssembly.GetTypes(); 24 var filterTypes = types.Where(x => !string.IsNullOrEmpty(x.Namespace)); 25 var result = filterTypes.Where(x => !x.IsAbstract && x.GetInterfaces() 26 .Any(y => y.GetTypeInfo() 27 .IsGenericType && y.GetGenericTypeDefinition() == typeof(IEntityTypeConfiguration<>))); 28 29 foreach (var configurationInstance in result.Select(Activator.CreateInstance)) 30 { 31 modelBuilder.ApplyConfiguration((dynamic)configurationInstance); 32 } 33 } 34 }
4-注册依赖 (IOC:data,application)
4.1)ApplicationServiceCollectionExtensions:application类库中IOC相关注册扩展类
1 public static class ApplicationServiceCollectionExtensions 2 { 3 public static void ApplicationRegister(this IServiceCollection services) 4 { 5 services.AddScoped<IUserService, UserService>(); 6 services.AddScoped<IMemberService, MemberService>(); 7 services.AddScoped<IBlogService, BlogService>(); 8 services.AddScoped<IBlogCategoryService, BlogCategoryService>(); 9 } 10 }
4.2)EfCoreServiceCollectionExtensions :data类库中IOC相关注册扩展类
1 public static class EfCoreServiceCollectionExtensions 2 { 3 /// <summary> 4 /// 注册数据库上下文 5 /// </summary> 6 /// <typeparam name="TDbContext"></typeparam> 7 /// <param name="services"></param> 8 /// <param name="connectionString"></param> 9 public static void AddDbContext<TDbContext>(this IServiceCollection services, string connectionString) 10 where TDbContext : DbContext 11 { 12 //UseRowNumberForPaging() 用于分页 ms sql server版本低于2012 需要使用 13 services.AddDbContext<TDbContext>(options => options.UseSqlServer(connectionString, b => b.UseRowNumberForPaging()));// 14 } 15 16 /// <summary> 17 /// 接口注册 18 /// </summary> 19 /// <param name="services"></param> 20 public static void RegisterRepository(this IServiceCollection services) 21 { 22 services.AddScoped(typeof(IRepository<,>), typeof(RepositoryBase<,>)); 23 } 24 }
5-登录
5.1)添加IUserService 接口,UserService类,添加UserDto GetData(string userName, string password); 函数
5.2)添加 UserDto Model 类,实现GetData方法
5.3)添加 UserExtensions 扩展类
1 public static class UserExtensions 2 { 3 public static UserDto ToDto(this User user) 4 { 5 if (user == null) 6 return null; 7 return new UserDto 8 { 9 Id = user.Id, 10 IsActived = user.IsActived, 11 UserName = user.UserName 12 }; 13 } 14 15 public static User ToEntity(this UserDto user) 16 { 17 if (user == null) 18 return null; 19 return new User 20 { 21 Id = user.Id, 22 IsActived = user.IsActived, 23 UserName = user.UserName, 24 CreatedOn = DateTime.Now 25 }; 26 } 27 }
5.4) 添加 Account Controller
1 using System; 2 using System.Collections.Generic; 3 using System.Security.Claims; 4 using System.Threading.Tasks; 5 using GR.Blogs.AspNetCore.Security; 6 using GR.Blogs.AspNetCore.Users; 7 using GR.Blogs.AspNetCore.Dto; 8 using Microsoft.AspNetCore.Authentication; 9 using Microsoft.AspNetCore.Authentication.Cookies; 10 using Microsoft.AspNetCore.Authorization; 11 using Microsoft.AspNetCore.Mvc; 12 using GR.Blogs.AspNetCore.Users.Dto; 13 14 namespace GR.Blogs.AspNetCore.Web.Mvc.Controllers 15 { 16 [Authorize] 17 public class AccountController : Controller 18 { 19 private readonly IUserService _userService; 20 21 public AccountController(IUserService userService) 22 { 23 _userService = userService; 24 } 25 26 [AllowAnonymous] 27 public IActionResult Login(string ReturnUrl = null) 28 { 29 var model = new LoginViewModel(); 30 return View(model); 31 } 32 33 [AllowAnonymous] 34 [HttpPost] 35 [ValidateAntiForgeryToken] 36 public async Task<IActionResult> Login(string ReturnUrl, /*[Bind("UserName,Password")]*/ LoginViewModel model) 37 { 38 if (!ModelState.IsValid) 39 { 40 return View(model); 41 } 42 var user = _userService.GetData(model.UserName, MD5EncryptHelper.MD5Encrypt(model.Password)); 43 if (user == null) 44 { 45 ModelState.AddModelError("", "用户名或密码错误"); 46 return View(model); 47 } 48 49 await HttpContext.SignInAsync(CookieAuthenticationDefaults.AuthenticationScheme, GetUserClaimsPrincipal(user)); 50 if (ReturnUrl == null) 51 return RedirectToAction("index", "home"); 52 else 53 return Redirect(ReturnUrl); 54 } 55 56 57 private ClaimsPrincipal GetUserClaimsPrincipal(UserDto user) 58 { 59 var claims = new List<Claim>(); 60 claims.Add(new Claim(ClaimTypes.Name, user.UserName, ClaimValueTypes.String)); 61 claims.Add(new Claim(ClaimTypes.UserData, user.Id.ToString(), ClaimValueTypes.String)); 62 //这里写死了,正式的话,角色需要通过获取数据库角色,写入的 63 //claims.Add(new Claim(ClaimTypes.Role, "Administrator", ClaimValueTypes.String)); 64 // 65 var userIdentity = new ClaimsIdentity(CookieAuthenticationDefaults.AuthenticationScheme); 66 userIdentity.AddClaims(claims); 67 var userPrincipal = new ClaimsPrincipal(userIdentity); 68 return userPrincipal; 69 } 70 71 public async Task<IActionResult> Logout() 72 { 73 await HttpContext.SignOutAsync(CookieAuthenticationDefaults.AuthenticationScheme); 74 return RedirectToAction("index", "home"); 75 } 76 } 77 }
5.5) Startup 中配置 Cookie 验证 ,注册IOC
5.6)添加Login页面布局,样式是基于flatUI
1 @model GR.Blogs.AspNetCore.Dto.LoginViewModel 2 @{ 3 Layout = null; 4 } 5 6 <!DOCTYPE html> 7 8 <html lang="zh-CN"> 9 <head> 10 <meta charset="utf-8" /> 11 <meta http-equiv="X-UA-Compatible" content="IE=edge"> 12 <meta name="viewport" content="width=1000, initial-scale=1.0, maximum-scale=1.0"> 13 <title>登录 - GR博客系统</title> 14 <link href="~/lib/bootstrap/dist/css/bootstrap.css" rel="stylesheet" /> 15 <link href="~/lib/flatUI/dist/css/flat-ui.css" rel="stylesheet" /> 16 <link href="~/lib/flatUI/docs/assets/css/demo.css" rel="stylesheet" /> 17 <!-- HTML5 shim, for IE6-8 support of HTML5 elements. All other JS at the end of file. --> 18 <!--[if lt IE 9]> 19 <script src="~/lib/flatUI/dist/js/vendor/html5shiv.js"></script> 20 <script src="~/lib/flatUI/dist/js/vendor/respond.min.js"></script> 21 <![endif]--> 22 </head> 23 <body> 24 <div class="container"> 25 <div class="login" style="margin-top:100px;"> 26 <div class="login-screen"> 27 <div class="login-icon"> 28 <img src="~/lib/flatUI/dist/img/icons/png/Mail.png" alt="Welcome to Mail App" /> 29 <h4>Welcome to <small>Mail App</small></h4> 30 </div> 31 32 <form class="login-form" method="post"> 33 @Html.AntiForgeryToken() 34 <div class="form-group"> 35 <div asp-validation-summary="ModelOnly" class="text-danger"></div> 36 </div> 37 <div class="form-group"> 38 <input type="text" asp-for="UserName" class="form-control login-field" value="" placeholder="用户名"> 39 <label class="login-field-icon fui-user"></label> 40 <span asp-validation-for="UserName" class="text-danger"></span> 41 </div> 42 43 <div class="form-group"> 44 <input type="password" asp-for="Password" class="form-control login-field" value="" placeholder="密码"> 45 <label class="login-field-icon fui-lock"></label> 46 <span asp-validation-for="Password" class="text-danger"></span> 47 </div> 48 49 <input type="submit" class="btn btn-primary btn-lg btn-block" value="登录"> 50 <a class="login-link" href="#">忘记密码?</a> 51 </form> 52 </div> 53 </div> 54 </div> 55 @*<environment include="Development"> 56 <script src="~/lib/jquery/dist/jquery.js"></script> 57 <script src="~/lib/bootstrap/dist/js/bootstrap.js"></script> 58 <script src="~/js/site.js" asp-append-version="true"></script> 59 </environment> 60 <environment exclude="Development"> 61 <script src="https://ajax.aspnetcdn.com/ajax/jquery/jquery-2.2.0.min.js" 62 asp-fallback-src="~/lib/jquery/dist/jquery.min.js" 63 asp-fallback-test="window.jQuery" 64 crossorigin="anonymous" 65 integrity="sha384-K+ctZQ+LL8q6tP7I94W+qzQsfRV2a+AfHIi9k8z8l9ggpc8X+Ytst4yBo/hH+8Fk"> 66 </script> 67 <script src="https://ajax.aspnetcdn.com/ajax/bootstrap/3.3.7/bootstrap.min.js" 68 asp-fallback-src="~/lib/bootstrap/dist/js/bootstrap.min.js" 69 asp-fallback-test="window.jQuery && window.jQuery.fn && window.jQuery.fn.modal" 70 crossorigin="anonymous" 71 integrity="sha384-Tc5IQib027qvyjSMfHjOMaLkfuWVxZxUPnCJA7l2mCWNIpG9mGCD8wGNIcPD7Txa"> 72 </script> 73 <script src="~/js/site.min.js" asp-append-version="true"></script> 74 </environment> 75 @{await Html.RenderPartialAsync("_ValidationScriptsPartial");}*@ 76 77 </body> 78 </html>
5-结束了
文笔不行,不知道该怎么组织语言,所以就以流水账形式记录了