【分分鍾內搭建一個帶用戶系統的博客程序(一)用戶系統】asp.net core的Identity真香,EF真香!


不用不知道,一用香到爆。

老哥是個屌絲前端,但也想寫點web應用耍一耍。之前弄過了NodeJs,也弄過JAVA,最近由於寫游戲的原因用C#,索性上手一波asp.net core。

這篇博客記錄的是,如何在分分鍾內搭建一個博客程序。包括:

  1. 發博客
  2. 看博客
  3. 用戶注冊登錄
  4. 用戶權限設置。

其中用的就是微軟提供的EntityFrame和Identity類庫。簡直他媽爽出翔。

1.快速生成一個項目

反正增刪改查就那么回事兒,快速生成一個項目模板才是主要的。

我不想創建一個帶Razor頁面的項目。因為我只需要API。老夫可是個前端!

 

這個時候按F5運行網站,然后就可以用Postman向 http://localhost:55536/api/values發送請求了。如果有過開發經驗的人一眼就能看明白這是怎么回事兒。

 2.給程序添加用戶系統。

添加用戶系統的意思是,允許用戶注冊和登錄。

如果我用NodeJs或者Java,我就要開始寫數據庫了,甚至設計數據表。可是微軟已經把好用的東西給准備好了,那就是:Identity類庫。這個類庫老JB好了。我只需要輕輕點幾下,一套完備的用戶系統就能生成到我的代碼上。

 

 我現在來解釋一波我進行了什么操作。

1.剛才我加的一大堆東西,其實就是最開始創建項目的時候,“身份驗證”那一部分幫我們做的事。當時我沒選,現在我手動加上。

2.上面這個圖,“替代所有文件”這部分如果選中,框架會幫我們生成相應的業務邏輯和Html模板(當然是Razor模板)。

3.因為注冊登錄需要和數據庫交互,所以“新建數據庫上下文類”幫我們新生成了一個和數據庫交互的上下文類。這個類是EntityFramework提供的。巨牛逼巨方便。

4.“新建用戶類”,這沒什么好說的吧?這個用戶類用於和數據庫的用戶表進行對應。

 這下我們牛逼了。然后你會發現項目目錄里多了一些文件,這些都是asp.net core幫我們生成的。

可以隨便探索一下。那個Readme.txt文件可以讀一下。是一個指導手冊,告訴你接下來要怎么做。

3.那么接下來要怎么做?

如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

4.寫一波注冊用戶的API吧!

其實注冊的業務邏輯已經生成好了,直接拿來用就可以。去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給我們建立好的。

 

 5.用戶權限校驗

接下來我要做的是,給某個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提供給我們的工具!很方便。來看一波代碼吧。

5.1 如何給用戶添加一個Claim

我更改了一下注冊流程,每一個注冊用戶都被默認設置為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

 

 6.用戶登錄

 用戶登錄的原理是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" };
        }
    }
}
UserController.cs

 

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();
        }
    }
}
Startup.cs

現在用戶登錄以后就會得到一串JWT。以后每次發請求的時候在頭部附帶JWT,瀏覽器就會認出用戶的身份,並且方便的做權限驗證了。這里附上PostMan設置。美滋滋。

 

 

 7.試驗一波

 

 8.搞定和額外說明

  • 用戶注冊默認是依靠UserName來注冊的。

其實用戶系統才是最大的門檻。至於帖子的增刪改查。可以用很簡單的一篇博客就能搞定了。祝你開心。

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM