当前位置:首页 > 后端开发 > 正文内容

ASP.NET Core 标识(Identity)结构系列(三):在 ASP.NET Core Web API 项目中运用标识(Identity)结构进行身份验证

邻居的猫1个月前 (12-09)后端开发982

Coding-7

前语:JWT完结登录的流程

  1. 客户端向服务器端发送用户名、暗码等恳求登录。
  2. 服务器端校验用户名、暗码,假如校验成功,则从数据库中取出这个用户的ID、人物等用户相关信息。
  3. 服务器端选用只需服务器端才知道的密钥来对用户信息的 JSON 字符串进行签名,构成签名数据。
  4. 服务器端把用户信息的 JSON 字符串和签名拼接到一同构成JWT,然后发送给客户端。
  5. 客户端保存服务器端回来的 JWT,而且在客户端每次向服务器端发送恳求的时分都带上这个 JWT。
  6. 每次服务器端收到浏览器恳求中带着的 JWT 后,服务器端用密钥对JWT的签名进行校验,假如校验成功,服务器端则从 JWT 中的 JSON 字符串中读取出用户的信息。

Step By Step 过程

  1. 创立一个 Asp.Net Core WebApi 项目

  2. 引证以下 Nuget 包:

    Microsoft.AspNetCore.Authentication.JwtBearer
    Microsoft.AspNetCore.Identity.EntityFrameworkCore
    Microsoft.EntityFrameworkCore.SqlServer
    Microsoft.EntityFrameworkCore.Tools

  3. 翻开 appsettings.json 文件,装备数据库衔接字符串和JWT的密钥、过期时刻

    {
      "Logging": {
    	"LogLevel": {
    	  "Default": "Information",
    	  "Microsoft.AspNetCore": "Warning"
    	}
      },
      "AllowedHosts": "*",
      "ConnectionStrings": {
    	"Default": "Server=(localdb)\\mssqllocaldb;Database=IdentityTestDB;Trusted_Connection=True;MultipleActiveResultSets=true"
      },
      "JWT": {
    	"SigningKey": "fasdfad&9045dafz222#fadpio@0232",
    	"ExpireSeconds": "86400"
      }
    }
    
  4. 创立JWT装备实体类 JWTOptions

    public class JWTOptions
    {
    	public string SigningKey { get; set; }
    	public int ExpireSeconds { get; set; }
    }
    
  5. 翻开 Program.cs 文件,在 builder.Build 之前,编写代码对 JWT 进行装备

    // 注入 JWT 装备
    services.Configure<JWTOptions>(builder.Configuration.GetSection("JWT"));
    
    // 注入 JwtBearer 装备
    services.AddAuthentication(JwtBearerDefaults.AuthenticationScheme)
    	.AddJwtBearer(x => { 
    		var jwtOpt = builder.Configuration.GetSection("JWT").Get<JWTOptions>();
    		byte[] keyBytes = Encoding.UTF8.GetBytes(jwtOpt.SigningKey);
    		var secKey = new SymmetricSecurityKey(keyBytes);
    		x.TokenValidationParameters = new()
    		{
    			ValidateIssuer = false,
    			ValidateAudience = false,
    			ValidateLifetime = true,
    			ValidateIssuerSigningKey = true,
    			IssuerSigningKey = secKey
    		};
    	});
    
  6. 翻开 Program.cs 文件,在 app.UseAuthorization 之前,增加身份验证中间件

    // 运用 Authentication 中间件,放在 UseAuthorization 之前
    app.UseAuthentication();
    
  7. 创立承继 IdentityRole 的 User 和 Role 实体类

    using Microsoft.AspNetCore.Identity;
    
    public class User: IdentityUser<long>
    {
    	public DateTime CreationTime { get; set; }
    	public string? NickName { get; set; }
    }
    
    public class Role: IdentityRole<long>
    {
    
    }
    
  8. 创立承继 IdentityDbContext 的上下文类

    using Microsoft.EntityFrameworkCore;
    using Microsoft.AspNetCore.Identity.EntityFrameworkCore;
    
    public class IdDbContext: IdentityDbContext<User, Role, long>
    {
    	public IdDbContext(DbContextOptions<IdDbContext> options) : base(options)
    	{
    
    	}
    
    	protected override void OnModelCreating(ModelBuilder modelBuilder)
    	{
    		base.OnModelCreating(modelBuilder);
    		modelBuilder.ApplyConfigurationsFromAssembly(this.GetType().Assembly);
    	}
    }
    
  9. (可选)假如数据表还没创立,履行数据库搬迁指令

    • 参阅前文《ASP.NET Core 标识(Identity)结构系列(一):怎么运用标识(Identity)创立用户和人物?》
  10. 创立登录恳求的参数实体类 LoginRequest

    public record LoginRequest(string UserName, string Password);
    
  11. 翻开登录恳求控制器,编写 Login API,在其间创立 JWT

    using Microsoft.AspNetCore.Identity;
    using Microsoft.AspNetCore.Mvc;
    using Microsoft.Extensions.Options;
    using Microsoft.IdentityModel.Tokens;
    using System.IdentityModel.Tokens.Jwt;
    using System.Security.Claims;
    using System.Text;
    
    namespace ASPNETCore_JWT1.Controllers
    {
    	[ApiController]
    	[Route("[controller]/[action]")]
    	public class Test1Controller : ControllerBase
    	{
    		private readonly UserManager<User> userManager;
    
    		//注入 UserManager
    		public Test1Controller(UserManager<User> userManager)
    		{
    			this.userManager = userManager;
    		}
    
    		// 生成 JWT
    		private static string BuildToken(IEnumerable<Claim> claims, JWTOptions options)
    		{
    			DateTime expires = DateTime.Now.AddSeconds(options.ExpireSeconds);
    			byte[] keyBytes = Encoding.UTF8.GetBytes(options.SigningKey);
    			var secKey = new SymmetricSecurityKey(keyBytes);
    			var credentials = new SigningCredentials(secKey, SecurityAlgorithms.HmacSha256Signature);
    			var tokenDescriptor = new JwtSecurityToken(
    				expires: expires, signingCredentials: 
    				credentials, 
    				claims: claims);
    			var result = new JwtSecurityTokenHandler().WriteToken(tokenDescriptor); 
    			return result;
    		}
    
    		// 在办法中注入 IOptions<JWTOptions> 
    		// 只需求回来 JWT Token 即可,其它的身份验证中间件会处理
    		[HttpPost]
    		public async Task<IActionResult> Login(
    			LoginRequest req,
    			[FromServices] IOptions<JWTOptions> jwtOptions)
    		{
    			string userName = req.UserName;
    			string password = req.Password;
    			var user = await userManager.FindByNameAsync(userName);
    			if (user == null)
    			{
    				return NotFound($"用户名不存在{userName}");
    			}
    			if (await userManager.IsLockedOutAsync(user))
    			{
    				return BadRequest("LockedOut");
    			}
    			var success = await userManager.CheckPasswordAsync(user, password);
    			if (!success)
    			{
    				return BadRequest("Failed");
    			}
    			
    			var claims = new List<Claim>();
    			claims.Add(new Claim(ClaimTypes.NameIdentifier, user.Id.ToString()));
    			claims.Add(new Claim(ClaimTypes.Name, user.UserName));
    			var roles = await userManager.GetRolesAsync(user);
    			foreach (string role in roles)
    			{
    				claims.Add(new Claim(ClaimTypes.Role, role));
    			}
    
    			var jwtToken = BuildToken(claims, jwtOptions.Value);
    			return Ok(jwtToken);
    		}
    	}
    }
    
  12. 翻开其它控制器,在类上增加 [Authorize] 这个特性

    using Microsoft.AspNetCore.Mvc;
    using System.Security.Claims;
    using Microsoft.AspNetCore.Authorization;
    
    namespace ASPNETCore_JWT1.Controllers
    {
    	// [Authorize] 特性标识此控制器的办法需求身份授权才干拜访
    	// 授权中间件会处理其它的
    	[ApiController]
    	[Route("[controller]/[action]")]
    	[Authorize]
    	public class Test2Controller : Controller
    	{
    		[HttpGet]
    		public IActionResult Hello()
    		{
    			// ControllerBase中界说的ClaimsPrincipal类型的User特点代表当时登录用户的身份信息
    			// 能够经过ClaimsPrincipal的Claims特点取得当时登录用户的一切Claim信息
    			// this.User.Claims
    			
    			string id = this.User.FindFirst(ClaimTypes.NameIdentifier)!.Value;
    			string userName = this.User.FindFirst(ClaimTypes.Name)!.Value;
    			IEnumerable<Claim> roleClaims = this.User.FindAll(ClaimTypes.Role);
    			string roleNames = string.Join(",", roleClaims.Select(c => c.Value));
    			return Ok($"id={id},userName={userName},roleNames ={roleNames}");
    		}
    	}
    }
    
  13. 翻开 Program.cs 文件,装备 Swagger,支撑发送 Authorization 报文头

    // 装备 Swagger 支撑 Authorization
    builder.Services.AddSwaggerGen(c => {
    	var scheme = new OpenApiSecurityScheme()
    	{
    		Description = "Authorization header. \r\nExample: 'Bearer 12345abcdef'",
    		Reference = new OpenApiReference
    		{
    			Type = ReferenceType.SecurityScheme,
    			Id = "Authorization"
    		},
    		Scheme = "oauth2",
    		Name = "Authorization",
    		In = ParameterLocation.Header,
    		Type = SecuritySchemeType.ApiKey
    	};
    	c.AddSecurityDefinition("Authorization", scheme);
    	var requirement = new OpenApiSecurityRequirement();
    	requirement[scheme] = new List<string>();
    	c.AddSecurityRequirement(requirement);
    });
    

发动项目,进行测验

  1. 首要拜访/Test1/Login,获取 JWT Token,仿制下这个值
  2. 然后拜访/Test2/Hello,不带 JWT Token,将收到 401 信息
  3. 在 Swagger 上的 Authorization 输入 JWT Token,从头拜访/Test2/Hello,将回来正确的成果
    • 假如是在 Postman 等第三方,要在 Header 上加上参数 Authorization=bearer

附录:完好的 Program 代码(要点留意代码中的注释

using Microsoft.AspNetCore.Identity;
using Microsoft.OpenApi.Models;
using Microsoft.EntityFrameworkCore;
using Microsoft.AspNetCore.Authentication.JwtBearer;
using System.Text;
using Microsoft.IdentityModel.Tokens;

var builder = WebApplication.CreateBuilder(args);

// Add services to the container.

builder.Services.AddControllers();
// Learn more about configuring Swagger/OpenAPI at https://aka.ms/aspnetcore/swashbuckle
builder.Services.AddEndpointsApiExplorer();

// 装备 Swagger 支撑 Authorization
builder.Services.AddSwaggerGen(c => {
    var scheme = new OpenApiSecurityScheme()
    {
        Description = "Authorization header. \r\nExample: 'Bearer 12345abcdef'",
        Reference = new OpenApiReference
        {
            Type = ReferenceType.SecurityScheme,
            Id = "Authorization"
        },
        Scheme = "oauth2",
        Name = "Authorization",
        In = ParameterLocation.Header,
        Type = SecuritySchemeType.ApiKey
    };
    c.AddSecurityDefinition("Authorization", scheme);
    var requirement = new OpenApiSecurityRequirement();
    requirement[scheme] = new List<string>();
    c.AddSecurityRequirement(requirement);
});

var services = builder.Services;
// 注册数据库服务
services.AddDbContext<IdDbContext>(opt => {
    string connStr = builder.Configuration.GetConnectionString("Default")!;
    opt.UseSqlServer(connStr);
});

// 注册数据维护服务
services.AddDataProtection();

// 注册 IdentityCore 服务
services.AddIdentityCore<User>(options => { 
    options.Password.RequireDigit = false;
    options.Password.RequireLowercase = false;
    options.Password.RequireNonAlphanumeric = false;
    options.Password.RequireUppercase = false;
    options.Password.RequiredLength = 6;
    options.Tokens.PasswordResetTokenProvider = TokenOptions.DefaultEmailProvider;
    options.Tokens.EmailConfirmationTokenProvider = TokenOptions.DefaultEmailProvider;
});

// 注入UserManager、RoleManager等服务
var idBuilder = new IdentityBuilder(typeof(User), typeof(Role), services);
idBuilder.AddEntityFrameworkStores<IdDbContext>()
    .AddDefaultTokenProviders()
    .AddRoleManager<RoleManager<Role>>()
    .AddUserManager<UserManager<User>>();

// 注入 JWT 装备
services.Configure<JWTOptions>(builder.Configuration.GetSection("JWT"));

// 注入 JwtBearer 装备
services.AddAuthentication(JwtBearerDefaults.AuthenticationScheme)
    .AddJwtBearer(x => { 
        var jwtOpt = builder.Configuration.GetSection("JWT").Get<JWTOptions>();
        byte[] keyBytes = Encoding.UTF8.GetBytes(jwtOpt.SigningKey);
        var secKey = new SymmetricSecurityKey(keyBytes);
        x.TokenValidationParameters = new()
        {
            ValidateIssuer = false,
            ValidateAudience = false,
            ValidateLifetime = true,
            ValidateIssuerSigningKey = true,
            IssuerSigningKey = secKey
        };
    });

var app = builder.Build();

// Configure the HTTP request pipeline.
if (app.Environment.IsDevelopment())
{
    app.UseSwagger();
    app.UseSwaggerUI();
}

app.UseHttpsRedirection();

// 运用 Authentication 中间件,放在 UseAuthorization 之前
app.UseAuthentication();
app.UseAuthorization();

app.MapControllers();

app.Run();

总结

  1. 假如其间某个操作办法不想被验证,能够在这个操作办法上增加 [AllowAnonymous] 特性
  2. 关于客户端取得的 JWT,在前端项目中,能够把令牌保存到 Cookie、LocalStorage 等方位,然后在后续恳求中重复运用,而关于移动App、PC客户端,能够把令牌保存到装备文件中或许本地文件数据库中。当履行【退出登录】操作的时分,咱们只需在客户端本地把 JWT 删去即可。
  3. 在发送恳求的时分,只需依照 HTTP 的要求,把 JWT 依照 "Bearer {JWT Token}" 格局放到姓名为 Authorization 的恳求报文头中即可
  4. 从 Authorization 中取出令牌,而且进行校验、解析,然后把解析成果填充到 User 特点中,这一切都是 ASP.NET Core 完结的,不需求开发人员自己编写代码

往期精彩

  1. C# 静态类,高手不想让你知道的 15 个本相
  2. 封装一个 C# 规模判别函数,从此离别重复编写规模判别代码的烦恼
  3. 用 C# Stopwatch 计时,让代码功能飞起来!
  4. 轻装上阵,Visual Studio LocalDB:.NET 程序员的本地数据库神器
  5. 封装一个C#全能根底数据类型转化器,一招处理一切根底类型转化烦恼
  6. 闲话 .NET(7):.NET Core 能筛选 .NET FrameWork 吗?
  7. 常用的 4 种 ORM 结构(EF Core,SqlSugar,FreeSql,Dapper)比照总结
  8. C# AutoMapper 10个常用办法总结
  9. C# 7个办法比较两个目标是否持平
  10. C# 去掉字符串最终一个字符的 4 种办法

image

扫描二维码推送至手机访问。

版权声明:本文由51Blog发布,如需转载请注明出处。

本文链接:https://www.51blog.vip/?id=151

标签: .NET.NET Core
分享给朋友:

“ASP.NET Core 标识(Identity)结构系列(三):在 ASP.NET Core Web API 项目中运用标识(Identity)结构进行身份验证” 的相关文章

java 完成中英文拼写查看和过错纠正?可我只会写 CRUD 啊!

java 完成中英文拼写查看和过错纠正?可我只会写 CRUD 啊!

拼写纠正系列 NLP 中文拼写检测完成思路 NLP 中文拼写检测纠正算法收拾 NLP 英文拼写算法,假如提高 100W 倍的功用? NLP 中文拼写检测纠正 Paper java 完成中英文拼写查看和过错纠正?可我只会写 CRUD 啊! 一个提高英文单词拼写检测功用 1000 倍的算法? 单词拼写纠...

python能做什么,Python的广泛应用与无限可能

Python 是一种高级编程语言,因其简单易学、功能强大而广受欢迎。以下是 Python 能做的一些主要事情:1. Web 开发:Python 有许多流行的 Web 框架,如 Django 和 Flask,可以用来创建网站和 Web 应用程序。2. 数据分析:Python 有许多强大的库,如 Pan...

php代码混淆, 什么是PHP代码混淆?

php代码混淆, 什么是PHP代码混淆?

PHP代码混淆(Obfuscation)是一种将代码转换为难以阅读和理解的形式的技术,目的是保护代码不被未经授权的人轻易理解和篡改。这通常用于保护软件的知识产权,防止他人窃取或逆向工程。1. 变量和函数重命名:将变量和函数的名称替换为无意义的字符或数字,使代码更难以理解。2. 代码合并:将多个文件合...

python应用领域,Python应用领域的广泛探索与未来展望

python应用领域,Python应用领域的广泛探索与未来展望

1. Web开发:Python拥有许多流行的Web框架,如Django、Flask等,这些框架可以帮助开发者快速构建Web应用程序。2. 数据分析:Python在数据分析领域非常受欢迎,拥有Pandas、NumPy、SciPy等强大的数据分析库。这些库提供了丰富的数据操作和分析功能,使得Python...

配置java环境变量

配置Java环境变量通常包括设置`JAVA_HOME`环境变量、`PATH`环境变量以及`CLASSPATH`环境变量。以下是在Windows系统上配置Java环境变量的步骤:1. 下载并安装Java: 访问Oracle官方网站下载Java Development Kit 。 安装JDK...

go2cn购途市场女鞋,GO2.CN购途市场——引领女鞋批发新潮流

go2cn购途市场女鞋,GO2.CN购途市场——引领女鞋批发新潮流

购途网(go2.cn)是一个专注于女鞋批发的B2B贸易信息服务平台,主要面向全国女鞋生产企业、批发商、网络分销商和实体门店等。该平台依托于成都女鞋产业带,提供以下主要服务:1. 女鞋货源信息:购途网汇聚了众多优质女鞋货源,包括来自成都美博城的所有女鞋厂家的货源信息,为采购商提供方便快捷的货源信息展示...