<kbd id="afajh"><form id="afajh"></form></kbd>
<strong id="afajh"><dl id="afajh"></dl></strong>
    <del id="afajh"><form id="afajh"></form></del>
        1. <th id="afajh"><progress id="afajh"></progress></th>
          <b id="afajh"><abbr id="afajh"></abbr></b>
          <th id="afajh"><progress id="afajh"></progress></th>

          《ASP.NET Core 與 RESTful API 開(kāi)發(fā)實(shí)戰(zhàn)》-- (第8章)-- 讀書(shū)筆記(中)

          共 11445字,需瀏覽 23分鐘

           ·

          2020-08-13 13:18

          第 8 章 認(rèn)證和安全

          8.2 ASP.NET Core Identity

          Identity 是 ASP.NET Core 中提供的對(duì)用戶和角色等信息進(jìn)行存儲(chǔ)與管理的系統(tǒng)

          Identity 由3層構(gòu)成,最底層為 Store 層,即存儲(chǔ)層,包含 IUserStore?接口與 IRoleStore?接口

          IUserStore?接口定義如下:

          namespace Microsoft.AspNetCore.Identity
          {
          public interface IUserStore : IDisposable where TUser : class
          {
          Task<string> GetUserIdAsync(TUser user, CancellationToken cancellationToken);

          Task<string> GetUserNameAsync(TUser user, CancellationToken cancellationToken);

          Task SetUserNameAsync(TUser user, string userName, CancellationToken cancellationToken);

          Task<string> GetNormalizedUserNameAsync(TUser user, CancellationToken cancellationToken);

          Task SetNormalizedUserNameAsync(
          TUser user,
          string normalizedName,
          CancellationToken cancellationToken);

          Task CreateAsync(
          TUser user,
          CancellationToken cancellationToken);

          Task UpdateAsync(
          TUser user,
          CancellationToken cancellationToken);

          Task DeleteAsync(
          TUser user,
          CancellationToken cancellationToken);

          Task FindByIdAsync(string userId, CancellationToken cancellationToken);

          Task FindByNameAsync(string normalizedUserName, CancellationToken cancellationToken);
          }
          }

          兩個(gè)接口定義極為類似,分別用來(lái)管理用戶與角色,在它們的定義中均包含了對(duì)各自的泛型參數(shù) TUser 和 TRole 的查找、創(chuàng)建、更新、刪除等數(shù)據(jù)讀取與存儲(chǔ)操作

          對(duì)于這兩個(gè)接口的實(shí)現(xiàn)將決定用戶與角色數(shù)據(jù)是如何存儲(chǔ)的,比如存儲(chǔ)在數(shù)據(jù)庫(kù)中或者文件中,甚至存儲(chǔ)在內(nèi)存中

          在 Microsoft.AspNetCore.Identity 中定義了兩種形式的 UserStoreBase 抽象類,它們均實(shí)現(xiàn)了 IUserStore

          public abstract class UserStoreBaseTKey, TUserClaim, TUserLogin, TUserToken> : IUserLoginStore, IUserStore, IDisposable, IUserClaimStore, IUserPasswordStore, IUserSecurityStampStore, IUserEmailStore, IUserLockoutStore, IUserPhoneNumberStore, IQueryableUserStore, IUserTwoFactorStore, IUserAuthenticationTokenStore, IUserAuthenticatorKeyStore, IUserTwoFactorRecoveryCodeStore
          where TUser : IdentityUser
          where TKey : IEquatable
          where TUserClaim : IdentityUserClaim, new()
          where TUserLogin : IdentityUserLogin, new()
          where TUserToken : IdentityUserToken, new()
          {
          。。。
          }

          public abstract class UserStoreBaseTRole, TKey, TUserClaim, TUserRole, TUserLogin, TUserToken, TRoleClaim> :
          UserStoreBase,
          IUserRoleStore
          where TUser : IdentityUser
          where TRole : IdentityRole
          where TKey : IEquatable
          where TUserClaim : IdentityUserClaim, new()
          where TUserRole : IdentityUserRole, new()
          where TUserLogin : IdentityUserLogin, new()
          where TUserToken : IdentityUserToken, new()
          where TRoleClaim : IdentityRoleClaim, new()
          {
          。。。
          }

          第一種僅處理對(duì)用戶的操作,第二種處理對(duì)用戶與角色的操作

          Identity 的第二層為 Managers 層,它包括 UserManager 與 RoleManager 兩個(gè)類,分別用于處理與用戶和角色相關(guān)的業(yè)務(wù)操作

          UserManager 的構(gòu)造函數(shù)如下:

          public class UserManager : IDisposable where TUser : class
          {
          public UserManager(
          IUserStore store,// 實(shí)現(xiàn)對(duì)用戶的存儲(chǔ)與讀取操作
          IOptions optionsAccessor,// 訪問(wèn)在程序中添加Identity服務(wù)時(shí)的IdentityOptions配置
          IPasswordHasher passwordHasher,// 用于創(chuàng)建密碼散列值以及驗(yàn)證密碼
          IEnumerable> userValidators,// 驗(yàn)證用戶的規(guī)則集合
          IEnumerable> passwordValidators,// 驗(yàn)證密碼的規(guī)則集合
          ILookupNormalizer keyNormalizer,// 用于對(duì)用戶名進(jìn)行規(guī)范化,從而便于查詢
          IdentityErrorDescriber errors,// 用于提供錯(cuò)誤信息
          IServiceProvider services,// 用于獲取需要的依賴
          ILogger> logger)// 用于記錄日志
          {
          。。。
          }
          }

          Identity 的最上層,即 Extensions 層,提供了一些輔助類(如 SignInManager 類),它包含了一系列與登錄相關(guān)的方法

          使用 Identity

          由于用戶和角色等數(shù)據(jù)均存儲(chǔ)在數(shù)據(jù)表中,因此需要?jiǎng)?chuàng)建一個(gè) EF Core 遷移,并通過(guò)該遷移在數(shù)據(jù)庫(kù)中創(chuàng)建與 Identity 相關(guān)的數(shù)據(jù)表

          namespace Library.API.Entities
          {
          public class User : IdentityUser
          {
          public DateTimeOffset BirthDate { get; set; }
          }
          }

          namespace Library.API.Entities
          {
          public class Role : IdentityRole
          {

          }
          }

          接下來(lái),修改 LibraryDbContext,使其派生自 IdentityDbContext 類,TKey 類型參數(shù)是用戶表與角色表主鍵字段的類型

          public class LibraryDbContext : IdentityDbContextRole, string>
          {
          。。。
          }

          需要添加 nuget 包:Microsoft.AspNetCore.Identity.EntityFrameworkCore

          接下來(lái),在 startup 中添加 Identity 服務(wù)

          services.AddIdentityRole>()
          .AddEntityFrameworkStores();

          AddIdentity 方法會(huì)向容器添加 UserManager、RoleManager,以及它們所依賴的服務(wù),并且會(huì)添加 Identity 用到的 Cookie 認(rèn)證

          AddEntityFrameworkStores 方法會(huì)將 EF Core 中對(duì) IUserStore?接口和 IroleStore?接口的實(shí)現(xiàn)添加到容器中

          添加 Identity 服務(wù)后,還應(yīng)修改添加 DbContext 服務(wù)的代碼為

          services.AddDbContext(
          config => config.UseSqlServer(Configuration.GetConnectionString("DefaultConnection"),
          optionBuilder => optionBuilder.MigrationsAssembly(typeof(Startup).Assembly.GetName().Name)));

          MigrationsAssembly 方法為當(dāng)前 DbContext 設(shè)置其遷移所在的程序集名稱,這是由于 DbContext 與為其創(chuàng)建的遷移并不在同一個(gè)程序集中

          接著,運(yùn)行以下命令

          Add-Migration AddIdentity
          Update-Database

          上述命令會(huì)創(chuàng)建一個(gè)名為 AddIdentity 的 EF Core 遷移,該遷移包含了創(chuàng)建與 Identity 相關(guān)的數(shù)據(jù)表操作,并將其修改應(yīng)用到數(shù)據(jù)庫(kù)中

          接下來(lái),在 AuthenticateController 中添加創(chuàng)建用戶的方法,并修改原來(lái)對(duì)用戶信息驗(yàn)證的邏輯

          首先創(chuàng)建 RegisterUser 類,在創(chuàng)建用戶時(shí),請(qǐng)求中的信息將會(huì)反序列化為此類型

          namespace Library.API.Models
          {
          public class RegisterUser
          {
          [Required, MinLength(4)]
          public string UserName { get; set; }

          [EmailAddress]
          public string Email { get; set; }

          [MinLength(6)]
          public string Password { get; set; }

          public DateTimeOffset BirthDate { get; set; }
          }
          }

          然后,在 AuthenticateController 中添加 AddUserAsync 方法,用于創(chuàng)建用戶

          public class AuthenticateController : ControllerBase
          {
          public IConfiguration Configuration { get; set; }
          public RoleManager RoleManager { get; set; }
          public UserManager UserManager { get; set; }

          public AuthenticateController(IConfiguration configuration, RoleManager roleManager, UserManager userManager)
          {
          Configuration = configuration;
          RoleManager = roleManager;
          UserManager = userManager;
          }

          [HttpPost("register", Name = nameof(AddUserAsync))]
          public async Task AddUserAsync(RegisterUser registerUser)
          {
          var user = new User
          {
          UserName = registerUser.UserName,
          Email = registerUser.Email,
          BirthDate = registerUser.BirthDate
          };

          IdentityResult result = await UserManager.CreateAsync(user, registerUser.Password);
          if (result.Succeeded)
          {
          return Ok();
          }
          else
          {
          ModelState.AddModelError("Error", result.Errors.FirstOrDefault()?.Description);
          return BadRequest(ModelState);
          }
          }

          。。。
          }

          接著添加一個(gè)根據(jù)用戶信息生成 Bearer Token 的方法

          [HttpPost("token2", Name = nameof(GenerateTokenAsync))]
          public async Task GenerateTokenAsync(LoginUser loginUser)
          {
          var user = await UserManager.FindByEmailAsync(loginUser.UserName);
          if (user == null)
          {
          return Unauthorized();
          }

          var result = UserManager.PasswordHasher.VerifyHashedPassword(user, user.PasswordHash, loginUser.Password);
          if (result != PasswordVerificationResult.Success)
          {
          return Unauthorized();
          }

          var userClaims = await UserManager.GetClaimsAsync(user);
          var userRoles = await UserManager.GetRolesAsync(user);
          foreach (var roleItem in userRoles)
          {
          userClaims.Add(new Claim(ClaimTypes.Role, roleItem));
          }

          var claims = new List
          {
          new Claim(JwtRegisteredClaimNames.Sub, user.UserName),
          new Claim(JwtRegisteredClaimNames.Jti, Guid.NewGuid().ToString()),
          new Claim(JwtRegisteredClaimNames.Email, user.Email)
          };

          claims.AddRange(userClaims);

          // 此處為生成token代碼,與GenerateToken方法中的內(nèi)容相同
          if (loginUser.UserName != "demouser" || loginUser.Password != "demopassword")
          {
          return Unauthorized();
          }

          //var claims = new List
          //{
          // new Claim(JwtRegisteredClaimNames.Sub,loginUser.UserName)
          //};

          var tokenConfigSection = Configuration.GetSection("Security:Token");
          var key = new SymmetricSecurityKey(Encoding.UTF8.GetBytes(tokenConfigSection["Key"]));
          var signCredential = new SigningCredentials(key, SecurityAlgorithms.HmacSha256);

          var jwtToken = new JwtSecurityToken(
          issuer: tokenConfigSection["Issuer"],
          audience: tokenConfigSection["Audience"],
          claims: claims,
          expires: DateTime.Now.AddMinutes(3),// 由于 JWT 不支持銷毀以及撤回功能,因此在設(shè)置它的有效時(shí)間時(shí),應(yīng)該設(shè)置一個(gè)較短的時(shí)間
          signingCredentials: signCredential);

          return Ok(new
          {
          token = new JwtSecurityTokenHandler().WriteToken(jwtToken),
          expiration = TimeZoneInfo.ConvertTimeFromUtc(jwtToken.ValidTo, TimeZoneInfo.Local)
          });
          }

          在上述方法中,首先驗(yàn)證用戶信息是否存在以及用戶信息是否正確,如果通過(guò)驗(yàn)證,則獲取該用戶相關(guān)的 Claim 以及角色,這些信息最終都會(huì)包含在生成的 Token 中

          運(yùn)行程序,注冊(cè)用戶,獲取用戶信息后請(qǐng)求 token2

          接下來(lái)介紹授權(quán)及其實(shí)現(xiàn)

          通過(guò) UserManager?類提供的方法可以將用戶添加到角色中,然而在這之前,需要先使用 RoleManager?創(chuàng)建相應(yīng)的角色

          private async Task AddUserToRoleAsync(User user, string roleName)
          {
          if (user == null || string.IsNullOrWhiteSpace(roleName))
          {
          return;
          }

          bool isRoleExist = await RoleManager.RoleExistsAsync(roleName);
          if (!isRoleExist)
          {
          await RoleManager.CreateAsync(new Role {Name = roleName});
          }
          else
          {
          if (await UserManager.IsInRoleAsync(user, roleName))
          {
          return;
          }
          }

          await UserManager.AddToRoleAsync(user, roleName);
          }

          當(dāng)創(chuàng)建用戶或管理用戶信息時(shí),調(diào)用上述方法即可將用戶添加到指定的角色中

          await AddUserToRoleAsync(user, "Administrator");

          當(dāng)把用戶添加到某一角色中時(shí),如果要使某一個(gè)接口僅被指定的角色訪問(wèn),那么只要在為其添加 [Authorize] 特性時(shí)指定 Roles 屬性即可

          [Authorize(Roles = "Administrator")]
          public class BookController : ControllerBase
          {
          。。。
          }

          允許多個(gè)角色訪問(wèn),可通過(guò)逗號(hào)分隔角色名

          [Authorize(Roles = "Administrator,Manager")]

          同時(shí)需要具有多個(gè)角色才能訪問(wèn)

          [Authorize(Roles = "Administrator")]
          [Authorize(Roles = "Manager")]

          基于 Claim 的授權(quán)則要求用戶必須具有某一個(gè)指定類型的 Claim,要實(shí)現(xiàn)基于 Claim 的授權(quán),需要?jiǎng)?chuàng)建授權(quán)策略并為其命名,然后在 [Authorize] 特性中指定 Policy 屬性

          要?jiǎng)?chuàng)建授權(quán)策略,只需在 startup 中添加并配置認(rèn)證服務(wù)

          services.AddMvc();
          services.AddAuthorization(options =>
          {
          options.AddPolicy("ManagerOnly", builder => builder.RequireClaim("ManagerId"));
          options.AddPolicy("LimitedUsers",
          builder => builder.RequireClaim("UserId", new string[] {"1", "2", "3"}));
          });

          上述方法添加了兩個(gè)授權(quán)策略,ManagerOnly 要求用戶必須具有類型為 ManagerId 的 Claim,而 LimitedUsers 則要求用戶必須具有類型為 UserId 的 Claim,且它的值必須為指定的值

          創(chuàng)建之后,只要在添加 [Authorize] 特性的時(shí)候指定 Policy 屬性即可

          [Authorize(Policy = "ManagerOnly")]

          復(fù)雜的授權(quán)策略需要通過(guò) IAuthorizationRequirement 接口和 AuthorizationHandler?類實(shí)現(xiàn)

          實(shí)現(xiàn)只有注冊(cè)日期超過(guò)3天后才有權(quán)限訪問(wèn)

          namespace Library.API.Policy
          {
          public class RegisteredMoreThen3DaysRequirement : AuthorizationHandler<RegisteredMoreThen3DaysRequirement>, IAuthorizationRequirement
          {
          protected override Task HandleRequirementAsync(AuthorizationHandlerContext context, RegisteredMoreThen3DaysRequirement requirement)
          {
          if (!context.User.HasClaim(cliam => cliam.Type == "RegisterDate"))
          {
          return Task.CompletedTask;
          }

          var regDate = Convert.ToDateTime(context.User.FindFirst(c => c.Type == "RegisterDate").Value);

          var timeSpan = DateTime.Now - regDate;
          if (timeSpan.TotalDays > 3)
          {
          context.Succeed(requirement);
          }

          return Task.CompletedTask;
          }
          }
          }

          要使用自定義策略,只要將它添加到 AuthorizationPolicyBuilder 類的集合屬性 Requirements 中即可

          services.AddAuthorization(options =>
          {
          options.AddPolicy("RegisteredMoreThen3DaysRequirement",
          builder => builder.Requirements.Add(new RegisteredMoreThen3DaysRequirement()));
          });

          之后通過(guò)特性指定策略名稱即可

          相關(guān)文章

          《ASP.NET Core 與 RESTful API 開(kāi)發(fā)實(shí)戰(zhàn)》-- (第8章)-- 讀書(shū)筆記(上)


          《ASP.NET Core 與 RESTful API 開(kāi)發(fā)實(shí)戰(zhàn)》-- (第7章)-- 讀書(shū)筆記(下)


          《ASP.NET Core 與 RESTful API 開(kāi)發(fā)實(shí)戰(zhàn)》-- (第7章)-- 讀書(shū)筆記(中)


          《ASP.NET Core 與 RESTful API 開(kāi)發(fā)實(shí)戰(zhàn)》-- (第7章)-- 讀書(shū)筆記(上)


          《ASP.NET Core 與 RESTful API 開(kāi)發(fā)實(shí)戰(zhàn)》-- (第6章)-- 讀書(shū)筆記(下)


          《ASP.ENT Core 與 RESTful API 開(kāi)發(fā)實(shí)戰(zhàn)》-- (第6章)-- 讀書(shū)筆記(上)


          《ASP.ENT Core 與 RESTful API 開(kāi)發(fā)實(shí)戰(zhàn)》-- (第5章)-- 讀書(shū)筆記(下)


          《ASP.ENT Core 與 RESTful API 開(kāi)發(fā)實(shí)戰(zhàn)》-- (第5章)-- 讀書(shū)筆記(中)


          《ASP.ENT Core 與 RESTful API 開(kāi)發(fā)實(shí)戰(zhàn)》-- (第5章)-- 讀書(shū)筆記(上)


          《ASP.ENT Core 與 RESTful API 開(kāi)發(fā)實(shí)戰(zhàn)》-- (第4章)-- 讀書(shū)筆記(下)


          《ASP.ENT Core 與 RESTful API 開(kāi)發(fā)實(shí)戰(zhàn)》-- (第4章)-- 讀書(shū)筆記(上)


          《ASP.ENT Core 與 RESTful API 開(kāi)發(fā)實(shí)戰(zhàn)》(第3章)-- 讀書(shū)筆記(下)


          《ASP.ENT Core 與 RESTful API 開(kāi)發(fā)實(shí)戰(zhàn)》(第3章)-- 讀書(shū)筆記(中)


          《ASP.ENT Core 與 RESTful API 開(kāi)發(fā)實(shí)戰(zhàn)》(第3章)-- 讀書(shū)筆記(上)


          《ASP.ENT Core 與 RESTful API 開(kāi)發(fā)實(shí)戰(zhàn)》-- 讀書(shū)筆記(第2章)


          《ASP.ENT Core 與 RESTful API 開(kāi)發(fā)實(shí)戰(zhàn)》-- 讀書(shū)筆記(第1章)




          歡迎各位讀者加入微信群一起學(xué)習(xí)交流,
          在公眾號(hào)后臺(tái)回復(fù)“加群”即可~~


          瀏覽 60
          點(diǎn)贊
          評(píng)論
          收藏
          分享

          手機(jī)掃一掃分享

          分享
          舉報(bào)
          評(píng)論
          圖片
          表情
          推薦
          點(diǎn)贊
          評(píng)論
          收藏
          分享

          手機(jī)掃一掃分享

          分享
          舉報(bào)
          <kbd id="afajh"><form id="afajh"></form></kbd>
          <strong id="afajh"><dl id="afajh"></dl></strong>
            <del id="afajh"><form id="afajh"></form></del>
                1. <th id="afajh"><progress id="afajh"></progress></th>
                  <b id="afajh"><abbr id="afajh"></abbr></b>
                  <th id="afajh"><progress id="afajh"></progress></th>
                  大香蕉伊人综合在线 | 日韩成人片。 | 无码视频免费观看 | 免费日逼的视频 | 午夜操逼逼 |