zoukankan      html  css  js  c++  java
  • JWT、用户授权与认证

    JWT:JSON Web Token ,作用:是用户授权(Authorization),指的是用户登录以后是否有权限访问特定的资源。错误状态码:403 forbidden

    用户的身份认证(Authentication):用户认证指的是使用用户名,密码来验证当前用户的身份→用户登录。错误状态码:401 Unauthorized

    JWT改变了传统的Session与cookie的有状态登录,替换cookie,JWT信息只需要保存在客户端,无状态登录,只解决授权的问题,跟登录没有关系;

    打开jwt.io 传入已经复制好的token进行解析:

    JWT有三个部分:

    header:头部,具体描述当前的JWT的编码算法,这个算法用于signature中数字签名的验证;

    payload:保存具体的用户信息;比如:id、姓名等等。iat表示JWT的创建时间,esp数据类型是毫秒,指的是JWT的有效时间;

    signature:激光防伪标志。服务器通过这个数字签名来判断你所发的token是否有效(数字签名使用的是非对称加密算法(hs256),只能使用服务器的私钥才解密);

    JWT优点:无状态,简单,方便,完美支持分布式部署;非对称加密,Token安全性高;

    JWT缺点:无状态,toeken一经发布则无法取消;                 明文传递,token安全性低(使用https可以解决);

    启用JWT无状态登录系统:

    一、首先安装JWT的框架:

    二、创建用户登录的token

    添加认证的Controller并在Controller中添加登录的API

    登录的参数:

    public class LoginDto
        {
            [Required]
            public string Email { get; set; }
            [Required]
            public string Password { get; set; }
        }
     [ApiController]
        [Route("auth")]
        public class AuthenticationController : ControllerBase
        {
            private readonly IConfiguration _configuration;  //注入读取配置文件的服务依赖
            private readonly UserManager<ApplicationUser> _userManager; //添加Has密码工具的服务依赖
            private readonly SignInManager<ApplicationUser> _signInManager;//处理用户的登录验证服务依赖
    
            public AuthenticationController(
                IConfiguration configuration,
                UserManager<ApplicationUser> userManager,
                SignInManager<ApplicationUser> signInManager
            )
            {
                _configuration = configuration;
                _userManager = userManager;
                _signInManager = signInManager;
            }
    
            [AllowAnonymous]
            [HttpPost("login")]
            public async Task<IActionResult> login([FromBody] LoginDto loginDto)
            {
                // 1 验证用户名密码
                var loginResult = await _signInManager.PasswordSignInAsync(
                    loginDto.Email,
                    loginDto.Password,
                    false,   //账号登录是否持久性
                    false    //报错是否把账号锁起来
                );
                if (!loginResult.Succeeded)
                {
                    return BadRequest();
                }
    
                var user = await _userManager.FindByNameAsync(loginDto.Email);  //验证成功取用户数据
    
                // 2 创建jwt
                // header
                var signingAlgorithm = SecurityAlgorithms.HmacSha256;//定义了数字签名算法
    
                // payload
                var claims = new List<Claim>  //JWT中自定义payload的数据
                {
                    // sub  用户ID
                    new Claim(JwtRegisteredClaimNames.Sub, user.Id),//把验证好的用户数据放到claims中
                    //new Claim(ClaimTypes.Role, "Admin")//用户角色
                };
                var roleNames = await _userManager.GetRolesAsync(user);//获得用户所有的角色
                foreach (var roleName in roleNames)
                {
                    var roleClaim = new Claim(ClaimTypes.Role, roleName);           //把用户列表转化为claim
                    claims.Add(roleClaim);//用来生成JWT token
                }
    
                // signiture  数字签名
                var secretByte = Encoding.UTF8.GetBytes(_configuration["Authentication:SecretKey"]);   //将私钥变成UTF8格式,私钥放在项目的配置文件中
                var signingKey = new SymmetricSecurityKey(secretByte);   //使用非对称加密算法将私钥进行加密
                var signingCredentials = new SigningCredentials(signingKey, signingAlgorithm);  //使用hs256验证加密后的私钥→跟header部分验证
    
                //使用上面的所有数据创建JWT的token
                var token = new JwtSecurityToken(
                    issuer: _configuration["Authentication:Issuer"],      //发布者
                    audience: _configuration["Authentication:Audience"],   //接收者(项目的前端)
                    claims,   //payload数据
                    notBefore: DateTime.UtcNow,  //发布时间
                    expires: DateTime.UtcNow.AddDays(1),   //有效时间
                    signingCredentials    //数字签名
                );
    
                var tokenStr = new JwtSecurityTokenHandler().WriteToken(token);//把token以字符串的形式输出
    
                // 3 return 200 ok + jwt
                return Ok(tokenStr);     //输出token
            }

    配置文件中的数据:

    三、启动授权API(使用已经创建的token来访问受保护的资源)

    注入JWT的身份验证服务,启动用户授权的框架:

    // 给项目注入JWT身份认证服务,同时启动用户授权的框架
                services.AddAuthentication(JwtBearerDefaults.AuthenticationScheme/*JWT认证类型*/)
                    .AddJwtBearer(options =>            //配置JWT认证
                    {
                        var secretByte = Encoding.UTF8.GetBytes(Configuration["Authentication:SecretKey"]);
                        options.TokenValidationParameters = new TokenValidationParameters()
                        {
                            ValidateIssuer = true,
                            ValidIssuer = Configuration["Authentication:Issuer"],   //验证token的发布者(后端)
    
                            ValidateAudience = true,                                //验证token的持有者(前端)
                            ValidAudience = Configuration["Authentication:Audience"],
    
                            ValidateLifetime = true,                                //验证token是否过期
    
                            IssuerSigningKey = new SymmetricSecurityKey(secretByte)     //加密私钥
                        };
                    });
            // This method gets called by the runtime. Use this method to configure the HTTP request pipeline.
            //服务框架的启动,注意启动的顺序
            public void Configure(IApplicationBuilder app, IWebHostEnvironment env)
            {
                if (env.IsDevelopment())
                {
                    app.UseDeveloperExceptionPage();
                }
    
                // 你在哪?
                app.UseRouting();
    
                // 你是谁?
                app.UseAuthentication();
    
                // 你可以干什么?有什么权限?
                app.UseAuthorization();
    
                app.UseEndpoints(endpoints =>
                {
                    
                    endpoints.MapControllers();
                });
            }

    给API加上用户角色授权:(凡是进行权限认证的Action都加上以下的特性)

    四、使用身份认证框架

    安装框架:

    注册框架的服务依赖:

    services.AddIdentity<ApplicationUser, IdentityRole>()
                    .AddEntityFrameworkStores<AppDbContext>();

    添加新的用户模型:

    public class ApplicationUser : IdentityUser
        {
            public string Address { get; set; }
            // ShoppingCart
            // Order
    
            public virtual ICollection<IdentityUserRole<string>> UserRoles { get; set; }
            public virtual ICollection<IdentityUserClaim<string>> Claims { get; set; }
            public virtual ICollection<IdentityUserLogin<string>> Logins { get; set; }
            public virtual ICollection<IdentityUserToken<string>> Tokens { get; set; }
        }

    初始化数据库,添加用户表,角色表等:

    public class AppDbContext : IdentityDbContext<ApplicationUser>
        {
            /// <summary>
            /// 注入DbContext实例,这个实例可以通过构建函数的实例传递进来
            /// </summary>
            /// <param name="options"></param>
            public AppDbContext(DbContextOptions<AppDbContext> options) : base(options)
            {
    
            }
            // 初始化用户与角色的种子数据
                // 1. 更新用户与角色的外键关系
                modelBuider.Entity<ApplicationUser>(b => {
                    b.HasMany(x => x.UserRoles)
                    .WithOne()
                    .HasForeignKey(ur => ur.UserId)
                    .IsRequired();
                });
    // 2. 添加角色 var adminRoleId = "308660dc-ae51-480f-824d-7dca6714c3e2"; // guid modelBuider.Entity<IdentityRole>().HasData( new IdentityRole { Id = adminRoleId, Name = "Admin", NormalizedName = "Admin".ToUpper() } ); // 3. 添加用户 var adminUserId = "90184155-dee0-40c9-bb1e-b5ed07afc04e"; ApplicationUser adminUser = new ApplicationUser { Id = adminUserId, UserName = "admin*********.com", NormalizedUserName = "admin*********.com".ToUpper(), Email = "admin*******.com", NormalizedEmail = "admin*******.com".ToUpper(), TwoFactorEnabled = false, EmailConfirmed = true, PhoneNumber = "123456789", PhoneNumberConfirmed = false }; PasswordHasher<ApplicationUser> ph = new PasswordHasher<ApplicationUser>(); //密码的Hash工具 adminUser.PasswordHash = ph.HashPassword(adminUser, "******");//hash密码 modelBuider.Entity<ApplicationUser>().HasData(adminUser); // 4. 给用户加入管理员权限 // 通过使用 linking table:IdentityUserRole modelBuider.Entity<IdentityUserRole<string>>() .HasData(new IdentityUserRole<string>() { RoleId = adminRoleId, UserId = adminUserId }); base.OnModelCreating(modelBuider); } }

    五、使用命令行更新数据库

    六、添加用户注册API:

    注册的参数:

    public class RegisterDto
        {
            [Required]
            public string Email { get; set; }
            [Required]
            public string Password { get; set; }
            [Required]
            [Compare(nameof(Password), ErrorMessage = "密码输入不一致")]
            public string ConfirmPassword { get; set; }
        }
    //private readonly UserManager<ApplicationUser> _userManager; //添加Has密码工具的服务依赖

    [AllowAnonymous] [HttpPost(
    "register")] public async Task<IActionResult> Register([FromBody] RegisterDto registerDto) { // 1 使用用户名创建用户对象 var user = new ApplicationUser() { UserName = registerDto.Email, Email = registerDto.Email }; // 2 hash密码,保存用户 var result = await _userManager.CreateAsync(user, registerDto.Password); if (!result.Succeeded) { return BadRequest(); } // 3 return return Ok(); }

    以上整个用户注册、登录、认证、授权就完成了,可能顺序有点乱,因为我先是利用假数据登录→创建JWT Token→用户授权→添加数据库表→注册用户→真实数据登录认证→测试授权操作。

  • 相关阅读:
    您所熟悉的软件测试类型都有哪些?请试着分别比较这些不同的测试类型的区别与联系(如功能测试、性能测试……)
    你对测试最大的兴趣在哪里?为什么?
    LoadRunner分为哪三个模块?请简述各模块的主要功能。
    Django学习之视图层
    Django学习之路由层
    Django学习
    Django简介
    jQuery
    JavaScript--DOM,BOM
    前端基础之JavaScript
  • 原文地址:https://www.cnblogs.com/jf-ace/p/14659471.html
Copyright © 2011-2022 走看看