不用不知道,一用香到爆。html
老哥是个屌丝前端,但也想写点web应用耍一耍。以前弄过了NodeJs,也弄过JAVA,最近因为写游戏的缘由用C#,索性上手一波asp.net core。前端
这篇博客记录的是,如何在分分钟内搭建一个博客程序。包括:web
其中用的就是微软提供的EntityFrame和Identity类库。简直他妈爽出翔。算法
反正增删改查就那么回事儿,快速生成一个项目模板才是主要的。数据库
我不想建立一个带Razor页面的项目。由于我只须要API。老夫但是个前端!后端
这个时候按F5运行网站,而后就能够用Postman向 http://localhost:55536/api/values发送请求了。若是有过开发经验的人一眼就能看明白这是怎么回事儿。api
添加用户系统的意思是,容许用户注册和登陆。浏览器
若是我用NodeJs或者Java,我就要开始写数据库了,甚至设计数据表。但是微软已经把好用的东西给准备好了,那就是:Identity类库。这个类库老JB好了。我只须要轻轻点几下,一套完备的用户系统就能生成到个人代码上。app
我如今来解释一波我进行了什么操做。框架
1.刚才我加的一大堆东西,其实就是最开始建立项目的时候,“身份验证”那一部分帮咱们作的事。当时我没选,如今我手动加上。
2.上面这个图,“替代全部文件”这部分若是选中,框架会帮咱们生成相应的业务逻辑和Html模板(固然是Razor模板)。
3.由于注册登陆须要和数据库交互,因此“新建数据库上下文类”帮咱们新生成了一个和数据库交互的上下文类。这个类是EntityFramework提供的。巨牛逼巨方便。
4.“新建用户类”,这没什么好说的吧?这个用户类用于和数据库的用户表进行对应。
这下咱们牛逼了。而后你会发现项目目录里多了一些文件,这些都是asp.net core帮咱们生成的。
能够随便探索一下。那个Readme.txt文件能够读一下。是一个指导手册,告诉你接下来要怎么作。
如readme文件所说,一步一步来。我仍是贴出来readme文件:
Support for ASP.NET Core Identity was added to your project - The code for adding Identity to your project was generated under Areas/Identity. Configuration of the Identity related services can be found in the Areas/Identity/IdentityHostingStartup.cs file. If your app was previously configured to use Identity, then you should remove the call to the AddIdentity method from your ConfigureServices method. //生成的UI须要静态文件支持,用下面这段代码使你的app支持静态文件 The generated UI requires support for static files. To add static files to your app: 1. Call app.UseStaticFiles() from your Configure method //用下面这段代码开启身份认证功能 To use ASP.NET Core Identity you also need to enable authentication. To authentication to your app: 1. Call app.UseAuthentication() from your Configure method (after static files) //生成的UI须要MVC支持,用这面这段代码开启MVC功能 The generated UI requires MVC. To add MVC to your app: 1. Call services.AddMvc() from your ConfigureServices method 2. Call app.UseMvc() from your Configure method (after authentication) //生成的数据库结构须要你执行Migration来同步数据库 The generated database code requires Entity Framework Core Migrations. Run the following commands:
//在cmd中执行下面两个命令 1. dotnet ef migrations add CreateIdentitySchema 2. dotnet ef database update
//或者 在包管理命令行执行下面两个命令 Or from the Visual Studio Package Manager Console: 1. Add-Migration CreateIdentitySchema 2. Update-Database Apps that use ASP.NET Core Identity should also use HTTPS. To enable HTTPS see https://go.microsoft.com/fwlink/?linkid=848054.
按照上面的操做来撸好app之后。框架就搭成了。牛逼到爆。一行代码都没写,一个很是完备的基础架子已经OK了。
须要注意的是,你要额外安装EntityFramework类库。这个百度教程太多了。我就不说了。
当你执行完那两个命令后,你会发现你的数据库里多了一些表。酷!成功了。
注:在这里执行命令的时候可能会说EntityFramework没安装什么的这时候不要虚,仔细看输出,会说你装了EF6和EFCore,你要指定一下用哪一个EF来运行命令,asp.net core的话就用 EntityFrameworkCore\Update-Database
其实注册的业务逻辑已经生成好了,直接拿来用就能够。去Areas/Identity/Pages/Account/Register.cshtml里面,能够看到这段代码。稍微改动一下就能够拿来用了。
首先是,我打算用Postman模拟用户前端的输入,后端在注册的时候接收3个值,邮箱,用户名,密码。因而俺建立一个类表明这个数据格式。强类型语言就是爽。
public class UserRegisterInput { public string UserName { get; set; } public string Email { get; set; } public string Password { get; set; } public bool RememberMe { get; set; } }
如今开始写controller。新建一个APIController,这个简直不用再描述了。最后Controller代码以下:
using System; using System.Collections.Generic; using System.Linq; using System.Threading.Tasks; using Microsoft.AspNetCore.Http; using Microsoft.AspNetCore.Identity; using Microsoft.AspNetCore.Mvc; using TinyBlog2.Areas.Identity.Data; using TinyBlog2.DTO; namespace TinyBlog2.Controllers { [Route("api/[controller]")] [ApiController] public class UserController : ControllerBase { private readonly UserManager<TinyBlog2User> _userManager; public UserController(UserManager<TinyBlog2User> userManager) { _userManager = userManager; } [HttpPost] [Route("Reg")] //这里是你的路由地址 post发往 https://localhost:55683/api/user/reg public async Task<IActionResult> Post([FromBody] UserRegisterInput Input) { var user = new TinyBlog2User { UserName = Input.UserName, Email = Input.Email }; var result = await _userManager.CreateAsync(user, Input.Password); if (result.Succeeded) { return Ok("注册成功"); } else { return BadRequest(); } } [HttpGet] public ActionResult<IEnumerable<string>> Get() { return new string[] { "value1", "value2" }; } } }
而后我用Postman发一波请求试试。
注册的最后,查看数据库,你的用户显然已经存在数据库里了。这些数据表都是asp.net core + EntityFramework给咱们创建好的。
接下来我要作的是,给某个Action增长权限校验,说句白话就是,有的接口我但愿登陆用户才能访问,有的接口我但愿管理员才能访问,或者有的接口我但愿只有付费Vip才能访问。怎么作呢?
这里用已经存在的ViewController来举例子。目前为止,ValueController的数据是谁均可以访问的。可是我来加一行代码,就一行!
[HttpGet] [Authorize(Policy = "VipOnly")]//很明显,今后这个Action只能是Vip才能访问。 public ActionResult<IEnumerable<string>> Get() { return new string[] { "value1", "value2" }; }
显然我定制了一个策略,这个策略名字叫作VipOnly。那么接下来我要定义这个策略。
public void ConfigureServices(IServiceCollection services) { services.AddAuthorization(options => { options.AddPolicy("VipOnly", policy => policy.RequireClaim("Role", "VipUser")); }); services.AddMvc().SetCompatibilityVersion(CompatibilityVersion.Version_2_1); }
我在startup.cs里面加入这一行代码,应该显而易见。意思是:增长一个名为VipOnly的策略,这个策略的要求是,若是用户有一个属性Role,这个Role的值是VipUser,那么就符合这个策略。
Claim就是用户的一个属性。这个属性能够在任什么时候候建立。这个Claim也是Asp.net core提供给咱们的工具!很方便。来看一波代码吧。
我更改了一下注册流程,每个注册用户都被默认设置为VipUser
[HttpPost] [Route("Reg")] public async Task<IActionResult> Post([FromBody] UserRegisterInput Input) { var user = new TinyBlog2User { UserName = Input.UserName, Email = Input.Email }; var result = await _userManager.CreateAsync(user, Input.Password); //给用户增长一个Claim var addClaimResult = await _userManager.AddClaimAsync(user, new System.Security.Claims.Claim("Role", "VipUser")); if (result.Succeeded) { return Ok("注册成功"); } else { return BadRequest(); } }
简单到爆炸不是吗?我如今注册一个用户,就会看到这个用户被添加了一个Claim,Role=VipUser
用户登陆的原理是JWT。是一个独立知识点。这里我只提供代码。教程网上一堆。须要我写的话请留言,我再补充。
using System; using System.Collections.Generic; using System.IdentityModel.Tokens.Jwt; using System.Linq; using System.Security.Claims; using System.Text; using System.Threading.Tasks; using Microsoft.AspNetCore.Http; using Microsoft.AspNetCore.Identity; using Microsoft.AspNetCore.Mvc; using Microsoft.Extensions.Configuration; using Microsoft.IdentityModel.Tokens; using TinyBlog2.Areas.Identity.Data; using TinyBlog2.DTO; namespace TinyBlog2.Controllers { [Route("api/[controller]")] [ApiController] public class UserController : ControllerBase { private readonly UserManager<TinyBlog2User> _userManager; private readonly IConfiguration _config; private readonly SignInManager<TinyBlog2User> _signInManager; public UserController(UserManager<TinyBlog2User> userManager, IConfiguration configuration, SignInManager<TinyBlog2User> signInManager) { _config = configuration; _signInManager = signInManager; _userManager = userManager; } [HttpPost] [Route("Reg")] public async Task<IActionResult> Post([FromBody] UserRegisterInput Input) { var user = new TinyBlog2User { UserName = Input.UserName, Email = Input.Email }; var result = await _userManager.CreateAsync(user, Input.Password); //给用户增长一个Claim var addClaimResult = await _userManager.AddClaimAsync(user, new System.Security.Claims.Claim("Role", "VipUser")); if (result.Succeeded && addClaimResult.Succeeded) { var key = new SymmetricSecurityKey(Encoding.UTF8.GetBytes(_config["Jwt:Key"])); //将加密后的密码用JWT指定算法进行加密 var creds = new SigningCredentials(key, SecurityAlgorithms.HmacSha256); //拿到当前登陆用户 TinyBlog2User currentUser = await _userManager.FindByEmailAsync(Input.Email); //获取当前用户的Claims IList<Claim> claimsList = await _userManager.GetClaimsAsync(currentUser); var unSecruityToken = new JwtSecurityToken(_config["Jwt:Issuer"], _config["Jwt:Issuer"], claimsList, expires: DateTime.Now.AddMinutes(30), signingCredentials: creds); var token = new JwtSecurityTokenHandler().WriteToken(unSecruityToken); return Ok(new { user = user, token = token }); } else { return BadRequest(); } } /// <summary> /// 先后端分离,前端的登陆请求发送到这里。 /// 返回200或者401,表明登陆成功和失败,若是登陆成功,返回一个token。 /// </summary> /// <param name="inputUser"></param> /// <returns> /// {"token": "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJlbWFpbCI6IjFAMS5jb20iLCJqdGkiOiI0ZDNiZGFjMC1hNjYzLTQwNTMtYjU1Yy02Njg2YjAyNjk0MmIiLCJFbWFpbCI6IjFAMS5jb20iLCJleHAiOjE1NDQxODgwMDcsImlzcyI6Imh0dHA6Ly9sb2NhbGhvc3Q6NjM5MzkvIiwiYXVkIjoiaHR0cDovL2xvY2FsaG9zdDo2MzkzOS8ifQ.GTFmUKiAfLTaOuv7rZ-g4Cns033RWehB8u3iFB59rFM"} /// </returns> [HttpPost] [Route("Login")] public async Task<IActionResult> Login([FromBody]UserLoginInput inputUser) { //拿到用户名和密码,用asp.net Core 自带的Identity来进行登陆 var result = await _signInManager.PasswordSignInAsync(inputUser.UserName, inputUser.Password, inputUser.RememberMe, lockoutOnFailure: true); if (result.Succeeded) { //把你本身的密码进行对称加密 var key = new SymmetricSecurityKey(Encoding.UTF8.GetBytes(_config["Jwt:Key"])); //将加密后的密码用JWT指定算法进行加密,这个加密算法有不少,能够去JWT官网上看 var creds = new SigningCredentials(key, SecurityAlgorithms.HmacSha256); //拿到当前登陆用户 TinyBlog2User user = await _userManager.FindByEmailAsync(inputUser.Email); //获取当前用户的Claims IList<Claim> claimsList = await _userManager.GetClaimsAsync(user); //用各类信息组成一个JWT var unSecruityToken = new JwtSecurityToken(_config["Jwt:Issuer"], _config["Jwt:Issuer"], claimsList, expires: DateTime.Now.AddMinutes(30), signingCredentials: creds); //把JWT加密一下返回给客户端 var token = new JwtSecurityTokenHandler().WriteToken(unSecruityToken); return Ok(new { token = token }); } else { return Unauthorized(); } } [HttpGet] public ActionResult<IEnumerable<string>> Get() { return new string[] { "value1", "value2" }; } } }
using System; using System.Collections.Generic; using System.Linq; using System.Text; using System.Threading.Tasks; using Microsoft.AspNetCore.Authentication.JwtBearer; using Microsoft.AspNetCore.Builder; using Microsoft.AspNetCore.Hosting; using Microsoft.AspNetCore.HttpsPolicy; using Microsoft.AspNetCore.Mvc; using Microsoft.Extensions.Configuration; using Microsoft.Extensions.DependencyInjection; using Microsoft.Extensions.Logging; using Microsoft.Extensions.Options; using Microsoft.IdentityModel.Tokens; namespace TinyBlog2 { 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.AddAuthentication(JwtBearerDefaults.AuthenticationScheme) .AddJwtBearer(options => { options.TokenValidationParameters = new TokenValidationParameters { ValidateIssuer = true, ValidateAudience = true, ValidateLifetime = true, ValidateIssuerSigningKey = true, ValidIssuer = Configuration["Jwt:Issuer"], ValidAudience = Configuration["Jwt:Issuer"], IssuerSigningKey = new SymmetricSecurityKey(Encoding.UTF8.GetBytes(Configuration["Jwt:Key"])) }; }); services.AddAuthorization(options => { options.AddPolicy("VipOnly", policy => policy.RequireClaim("Role", "VipUser")); }); services.AddMvc().SetCompatibilityVersion(CompatibilityVersion.Version_2_1); } // This method gets called by the runtime. Use this method to configure the HTTP request pipeline. public void Configure(IApplicationBuilder app, IHostingEnvironment env) { if (env.IsDevelopment()) { app.UseDeveloperExceptionPage(); } else { app.UseHsts(); } app.UseStaticFiles(); app.UseAuthentication(); app.UseHttpsRedirection(); app.UseMvc(); } } }
如今用户登陆之后就会获得一串JWT。之后每次发请求的时候在头部附带JWT,浏览器就会认出用户的身份,而且方便的作权限验证了。这里附上PostMan设置。美滋滋。
其实用户系统才是最大的门槛。至于帖子的增删改查。能够用很简单的一篇博客就能搞定了。祝你开心。