添加Identity数据上下文

安装nuget包:Microsoft.AspNetCore.Identity.EntityFrameworkCore

创建ApplicationDbContext类

创建一个Data文件夹,并创建一个新类,添加以下内容

    public class ApplicationDbContext : IdentityDbContext
{
public ApplicationDbContext(DbContextOptions options) : base(options)
{
}
}

这个是一个默认Identity上下文,继承于DbContext,包含了一些默认的表(Dbset)包括用户,权限身份等等。

注册数据库和数据上下文

添加以下代码到ConfigureServices中

            services.AddDbContext<ApplicationDbContext>(options =>
options.UseSqlServer(Configuration.GetConnectionString("DefaultConnection")));
            services.AddIdentity<IdentityUser,IdentityRole>()
          .AddEntityFrameworkStores<ApplicationDbContext>();

在.net core 3.1中,AddDefaultIdentity() 被删除了,所以这里用了AddIdentity()。

如果你需要使用SqlServer或者localdb的话,就安装nuget:Microsoft.EntityFrameworkCore.SqlServer ,我自己是使用的Mysql,使用的nuget包是:Pomelo.EntityFrameworkCore.MySql。

这两个包都会自动安装EFCore包。

添加链接字符串到appsettings.json

localdb

  "ConnectionStrings": {
"DefaultConnection": "Server=(localdb)\\MSSQLLocalDB;Database=AuthApiAndBlazor;Trusted_Connection=True;MultipleActiveResultSets=true"
},

Mysql

  "ConnectionStrings": {
"DefaultConnection": "Server=localhost;database=AuthApiAndBlazor;uid=root;pwd=password;charset=utf8;sslMode=None",
},

打开包管理器(或.Net CLI),添加迁移。

使用包管理员

PM> Add-Migration Init

使用命令行

C:\Users\Administrator\source\repos\AuthApiAndBlazor\server> dotnet ef migrations add init

当然,在3.1.2中,你可能会遇到错误,需要安装 Microsoft.EntityFrameworkCore.Design包

Your startup project 'server' doesn't reference Microsoft.EntityFrameworkCore.Design. This package is required for the Entity Framework Core Tools to work. Ensure your startup project is correct, install the package, and try again.

检查迁移文件,无误后更新到服务器

包管理器

PM> Update-Database

命令行

C:\Users\Administrator\source\repos\AuthApiAndBlazor\server> dotnet ef database update

添加用户验证到管道(Configure method)中

            app.UseAuthorization(); //验证用户可以做什么
app.UseAuthentication(); //用户身份的验证

为你的api 控制器添加授权验证(用户验证)来测试我们的用户成功了

    [Authorize] //添加特性
public class WeatherForecastController : ControllerBase

生成后运行,这时候再访问WeatherForecast的api就会跳转到https://localhost:5002/Account/Login?ReturnUrl=%2FWeatherForecast Login操作的页面上,这是Identity的预设链接,也是MVC标识(Identity)基架的默认页面,在MVC下这会被自动创建,但是由于我们是手动添加到WebApi中的,所以需要更改,我们也只需要它返回401状态码即可,实际登陆应该由前端进行操作。

设置JWT

首先需要安装Nuget包:Microsoft.AspNetCore.Authentication.JwtBearer

添加代码到ConfigureServices method中

            services.AddAuthentication((opts =>
{
opts.DefaultAuthenticateScheme = JwtBearerDefaults.AuthenticationScheme;
opts.DefaultChallengeScheme = JwtBearerDefaults.AuthenticationScheme; //如果不加这行,将会跳转到/Account/Login,也就是说你可以用视图来处理未登录
})) //设置验证方式为常量Bearer,即JWTBearer
.AddJwtBearer(options =>
{
options.TokenValidationParameters = new TokenValidationParameters
{
ValidateIssuer = true, //验证发行方
ValidateAudience = true, //验证听众api
ValidateLifetime = true, //验证生存时间
ValidateIssuerSigningKey = true, //验证发行方密钥
ValidIssuer = Configuration["JwtIssuer"], //发行方
ValidAudience = Configuration["JwtAudience"], //听众
IssuerSigningKey = new SymmetricSecurityKey(Encoding.UTF8.GetBytes(Configuration["JwtSecurityKey"])) //对称密钥
};
});

这里的写法是将JwtIssuer 、 JwtAudience 和 JwtSecurityKey 都放到appsettings.json文件中,你可以自己选择,我没有试过AddJwtBearer是单例还是怎样,即它可能不是实时可修改的。

它在appsettings.json的样子:

  "JwtSecurityKey": "RANDOM_KEY_MUST_NOT_BE_SHARED", //密钥
"JwtIssuer": "https://localhost:5002", //server的域名
"JwtAudience": "http://localhost:5001", //client (Blazor的域名)
"JwtExpiryInDays": //过期时间

添加Register和Login控制器

在server中生成两个api控制器,一个用于注册,一个用来做登陆。

RegisterController:

    [Route("api/[controller]")]
[ApiController]
public class AccountsController : ControllerBase
{
private readonly UserManager<IdentityUser> _userManager; public AccountsController(UserManager<IdentityUser> userManager)
{
_userManager = userManager;
} [HttpPost]
public async Task<IActionResult> Post([FromBody]RegisterModel model)
{
//创建用户,实际运用中我们一般会将IdentityUser更换为我们自定义的用户
var newUser = new IdentityUser { UserName = model.Email, Email = model.Email }; var result = await _userManager.CreateAsync(newUser, model.Password); if (!result.Succeeded)
{
var errors = result.Errors.Select(x => x.Description);
//提示失败
return Ok(new RegisterResult { Successful = false, Errors = errors });
}
//提示成功,让前端做跳转
return Ok(new RegisterResult { Successful = true });
}
}

所需模型: (tips:这里因为我们前后端都是使用.net core 去实现,所以实际上我们可以将这些前后端都需要的模型,做成一个共享类库,即保证了同步性,又节省了代码量,如果你不这么做的话,你接下来还需要再添加一次这些模型到client项目)

RegisterModel(用于接收注册前端参数)

    public class RegisterModel
{
[Required]
[EmailAddress]
[Display(Name = "Email")]
public string Email { get; set; } [Required]
[StringLength(, ErrorMessage = "The {0} must be at least {2} and at max {1} characters long.", MinimumLength = )]
[DataType(DataType.Password)]
[Display(Name = "Password")]
public string Password { get; set; } [DataType(DataType.Password)]
[Display(Name = "Confirm password")]
[Compare("Password", ErrorMessage = "The password and confirmation password do not match.")]
public string ConfirmPassword { get; set; }
}

RegisterResult (用于回传注册状态)

    public class RegisterResult
{
public bool Successful { get; set; }
public IEnumerable<string> Errors { get; set; }
}

为了方便,我们这里一次性也把登陆的模型都添加进去

LoginModel

    public class LoginModel
{
[Required]
public string Email { get; set; } [Required]
public string Password { get; set; } public bool RememberMe { get; set; }
}

LoginResult

    public class LoginResult
{
public bool Successful { get; set; }
public string Error { get; set; }
public string Token { get; set; } //最终token
}

UserModel

    public class UserModel
{
public string Email { get; set; }
public bool IsAuthenticated { get; set; }
}

LoginController的代码:

    [Route("api/[controller]")]
[ApiController]
public class LoginController : ControllerBase
{
private readonly IConfiguration _configuration;
private readonly SignInManager<IdentityUser> _signInManager; public LoginController(IConfiguration configuration,
SignInManager<IdentityUser> signInManager)
{
_configuration = configuration;
_signInManager = signInManager;
} //api/login (GET)
[HttpPost]
public async Task<IActionResult> Login([FromBody] LoginModel login)
{
//尝试登陆
var result = await _signInManager.PasswordSignInAsync(login.Email, login.Password, false, false); if (!result.Succeeded) return BadRequest(new LoginResult { Successful = false, Error = "Username and password are invalid." }); //如果登陆失败 //payload
var claims = new[]
{
new Claim(ClaimTypes.Name, login.Email)
}; //对称密钥
var key = new SymmetricSecurityKey(Encoding.UTF8.GetBytes(_configuration["JwtSecurityKey"]));
//密钥做签名
var creds = new SigningCredentials(key, SecurityAlgorithms.HmacSha256);
//过期时间
var expiry = DateTime.Now.AddDays(Convert.ToInt32(_configuration["JwtExpiryInDays"])); //生成token
var token = new JwtSecurityToken(
_configuration["JwtIssuer"],
_configuration["JwtAudience"],
claims,
expires: expiry,
signingCredentials: creds
); return Ok(new LoginResult { Successful = true, Token = new JwtSecurityTokenHandler().WriteToken(token) });
}
}

下面我们可以自己找个方法验证下,这里就省略。 如果正确的话,你的WeatherForecast控制器应该是401未授权的。

当然我在实际运行中试过并不返回401而是302跳转到了/Account/Login这个默认的登陆页面,由于我们将全部内容都交由前端处理,并不打算用mvc方式处理登陆注册,所以我们必须返回401以供前端识别。

使用postman做测试,首先WeatherForecast控制器是401返回(GET)

其次来试验/api/register 注册控制器

首先设置Content-Type为application/json

接着把json字符串放入body,经过实验,提交的json中对象名称可以首字母小写也可以首字母大写,跟实际类名相同都可,但是Blazor提交的json是会自动将首字母变小写,但是后面的其他的大写字母都不会变。当在使用blazor对接其他后端的时候要小心,因为如果要更改这个已经做好的过程,稍微有点麻烦

接下来可以获得Response

{
"successful": false,
"errors": [
"User name 'jiangsimpler@gmail.com' is already taken."
]
}

到此本章就结束了,下一章将开始处理client前端的注册登陆和登出以及查询授权api数据

Blazor client-side + webapi (.net core 3.1) 添加jwt验证流程(非host)第二步 添加Identity的更多相关文章

  1. Blazor client-side + webapi (.net core 3.1) 添加jwt验证流程(非host)第一步

    第一步,设置并检查CROS跨域请求 因为我们并不打算将Blazor 由webapi来进行host,所以Blazor和api将是两个域名,这样操作即方便以后单独使用Blazor来写前端,但后端采用已有或 ...

  2. Blazor client-side + webapi (.net core 3.1) 添加jwt验证流程(非host)第三章 客户端存储及验证

    准备工作: 安装Nuget包:Blazored.LocalStorge. 这是一个client-side 浏览器存储库,找了非常久. 官方文档中也有一款Microsoft.AspNetCore.Pro ...

  3. jwt-在asp.net core中的使用jwt

    JWT学习文章: 第一篇:JWT原理 第二篇:JWT原理实现代码 第三篇:在asp.net core中的使用JWT 前两篇文章中我写了jwt的原理,并且也用原理实现了jwt的验证.如果要看前两篇文章, ...

  4. .net core webapi+EF Core

    .net core webapi+EF Core 一.描述: EF Core必须下载.net core2.0版本 Micorsoft.EntityFrameworkCore:EF框架的核心包Micor ...

  5. 【Blazor】在ASP.NET Core中使用Blazor组件 - 创建一个音乐播放器

    前言 Blazor正式版的发布已经有一段时间了,.NET社区的各路高手也创建了一个又一个的Blazor组件库,其中就包括了我和其他小伙伴一起参与的AntDesign组件库,于上周终于发布了第一个版本0 ...

  6. ASP.NET Core 实战:基于 Jwt Token 的权限控制全揭露

    一.前言 在涉及到后端项目的开发中,如何实现对于用户权限的管控是需要我们首先考虑的,在实际开发过程中,我们可能会运用一些已经成熟的解决方案帮助我们实现这一功能,而在 Grapefruit.VuCore ...

  7. 【.NET Core项目实战-统一认证平台】第十二章 授权篇-深入理解JWT生成及验证流程

    [.NET Core项目实战-统一认证平台]开篇及目录索引 上篇文章介绍了基于Ids4密码授权模式,从使用场景.原理分析.自定义帐户体系集成完整的介绍了密码授权模式的内容,并最后给出了三个思考问题,本 ...

  8. ASP.NET Core Web Api之JWT刷新Token(三)

    前言 如题,本节我们进入JWT最后一节内容,JWT本质上就是从身份认证服务器获取访问令牌,继而对于用户后续可访问受保护资源,但是关键问题是:访问令牌的生命周期到底设置成多久呢?见过一些使用JWT的童鞋 ...

  9. 为 ASP.NET Core (6.0)服务应用添加ApiKey验证支持

    这个代码段演示了如何为一个ASP.NET Core项目中添加Apikey验证支持. 首先,通过下面的代码创建项目 dotnet new webapi -minimal -o yourwebapi 然后 ...

随机推荐

  1. RTMP、HTTP、HLS协议比较

    RTMP HLS HTTP 直播协议一次看个够 直播从2016年一路火到了2017年,如今要在自己的App里加入直播功能,只要找一个现成的SDK就行了,什么拍摄.美颜.推流,一条龙服务.不过作为直播身 ...

  2. 浅谈openresty

    浅谈openresty 为什么会有OpenResty? 我们都知道Nginx有很多的特性和好处,但是在Nginx上开发成了一个难题,Nginx模块需要用C开发,而且必须符合一系列复杂的规则,最重要的用 ...

  3. 温故知新,.NET 重定向深度分析

    在早期的.NET Framework程序员心里,重定向Redirect其实分为两种: Response.Redirect: Response对象的Redirect方法提供了一种实现客户端重定向的方法 ...

  4. 51Nod 1279 扔盘子 (思维+模拟)

    题意: 有口井,往里扔盘子,最多扔多少个 n<=5e5, 1s 思路: 如果比较高的地方井口比较小,那么下面的再大也没有用,只需要维护一个单调减的数组然后O(n+m)模拟即可 代码: #incl ...

  5. ssh_exchange_identification: read: Connection reset

    垃圾服务器,真的佛了,明明服务器从装的系统,连接半天连接不上,但是别人的电脑就可以,要使用xshell和fileshell链接,按照软件报的错误来修复的话,根本解决不了问题,还是得命令行ssh roo ...

  6. CAS 分析

    CAS是什么 (1) CAS(Compare and Swap) 比较并交换, 比较并交换是在多线程并发时用到的一种技术 (2) CAS是原子操作, 保证并发安全性, 而不是保证并发同步. (3) C ...

  7. 一次修改数据库物理文件造成Mysql宕机的恢复记录

    事件起始 某夜,我正在床上冥想准备入睡,忽然同事向我求救:消息内容如下: Oh My Gold 改了些配置,啥都没了!都没了!没了!了! 我仔细询问,原来是她因为某些原因将某库的物理文件夹改名后,发现 ...

  8. 今天第一天开通博客,随笔总结一下resultType(属性)和resultMap,collection和association,Statement和PreparedStatement各自的区别

    1.resultType(属性)和resultMap(标签引用)的区别? resultType不支持自定义返回结果,会将查询到的结果通过到type中java对象的同名的属性,对象中的属性名必须和数据库 ...

  9. rabbit MQ 消息队列

    为什么会需要消息队列(MQ)? 一.消息队列概述消息队列中间件是分布式系统中重要的组件,主要解决应用解耦,异步消息,流量削锋等问题,实现高性能,高可用,可伸缩和最终一致性架构.目前使用较多的消息队列有 ...

  10. 浅谈单利模式及其应用场景(Python)

    python 中的单利模式 使用场景: + Python的logger就是一个单例模式,用以日志记录 + Windows的资源管理器是一个单例模式 + 线程池,数据库连接池等资源池一般也用单例模式 + ...