网站首页 全球最实用的IT互联网站!

人工智能P2P分享Wind搜索发布信息网站地图标签大全

当前位置:诺佳网 > 软件工程 > 后端开发 > .Net >

ASP.NET Core 标识(Identity)框架系列(三):在

时间:2024-12-06 22:51

人气:

作者:admin

标签:

导读:一个在 ASP.NET Core Web API 项目中使用标识(Identity)框架进行身份验证的详细例子...

Coding-7

  1. 客户端向服务器端发送用户名、密码等请求登录。
  2. 服务器端校验用户名、密码,如果校验成功,则从数据库中取出这个用户的ID、角色等用户相关信息。
  3. 服务器端采用只有服务器端才知道的密钥来对用户信息的 JSON 字符串进行签名,形成签名数据。
  4. 服务器端把用户信息的 JSON 字符串和签名拼接到一起形成JWT,然后发送给客户端。
  5. 客户端保存服务器端返回的 JWT,并且在客户端每次向服务器端发送请求的时候都带上这个 JWT。
  6. 每次服务器端收到浏览器请求中携带的 JWT 后,服务器端用密钥对JWT的签名进行校验,如果校验成功,服务器端则从 JWT 中的 JSON 字符串中读取出用户的信息。
  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. (可选)如果数据表还没创建,执行数据库迁移命令

  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
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

温馨提示:以上内容整理于网络,仅供参考,如果对您有帮助,留下您的阅读感言吧!
相关阅读
本类排行
相关标签
本类推荐

CPU | 内存 | 硬盘 | 显卡 | 显示器 | 主板 | 电源 | 键鼠 | 网站地图

Copyright © 2025-2035 诺佳网 版权所有 备案号:赣ICP备2025066733号
本站资料均来源互联网收集整理,作品版权归作者所有,如果侵犯了您的版权,请跟我们联系。

关注微信