不用不知道,一用香到爆。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設置。美滋滋。
其實用戶系統纔是最大的門檻。至於帖子的增刪改查。能夠用很簡單的一篇博客就能搞定了。祝你開心。