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→用戶授權→添加數據庫表→注冊用戶→真實數據登錄認證→測試授權操作。

 


免責聲明!

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



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