1. <strong id="7actg"></strong>
    2. <table id="7actg"></table>

    3. <address id="7actg"></address>
      <address id="7actg"></address>
      1. <object id="7actg"><tt id="7actg"></tt></object>

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

        共 11445字,需瀏覽 23分鐘

         ·

        2020-08-13 13:18

        第 8 章 認證和安全

        8.2 ASP.NET Core Identity

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

        Identity 由3層構(gòu)成,最底層為 Store 層,即存儲層,包含 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);
        }
        }

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

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

        在 Microsoft.AspNetCore.Identity 中定義了兩種形式的 UserStoreBase 抽象類,它們均實現(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()
        {
        。。。
        }

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

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

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

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

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

        使用 Identity

        由于用戶和角色等數(shù)據(jù)均存儲在數(shù)據(jù)表中,因此需要創(chuàng)建一個 EF Core 遷移,并通過該遷移在數(shù)據(jù)庫中創(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
        {

        }
        }

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

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

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

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

        services.AddIdentityRole>()
        .AddEntityFrameworkStores();

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

        AddEntityFrameworkStores 方法會將 EF Core 中對 IUserStore?接口和 IroleStore?接口的實現(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)建的遷移并不在同一個程序集中

        接著,運行以下命令

        Add-Migration AddIdentity
        Update-Database

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

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

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

        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);
        }
        }

        。。。
        }

        接著添加一個根據(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è)置它的有效時間時,應(yīng)該設(shè)置一個較短的時間
        signingCredentials: signCredential);

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

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

        運行程序,注冊用戶,獲取用戶信息后請求 token2

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

        通過 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)建用戶或管理用戶信息時,調(diào)用上述方法即可將用戶添加到指定的角色中

        await AddUserToRoleAsync(user, "Administrator");

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

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

        允許多個角色訪問,可通過逗號分隔角色名

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

        同時需要具有多個角色才能訪問

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

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

        要創(chuàng)建授權(quán)策略,只需在 startup 中添加并配置認證服務(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"}));
        });

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

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

        [Authorize(Policy = "ManagerOnly")]

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

        實現(xiàn)只有注冊日期超過3天后才有權(quá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ān)文章

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


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


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


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


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


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


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


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


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


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


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


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


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


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


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


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




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


        瀏覽 60
        點贊
        評論
        收藏
        分享

        手機掃一掃分享

        分享
        舉報
        評論
        圖片
        表情
        推薦
        點贊
        評論
        收藏
        分享

        手機掃一掃分享

        分享
        舉報
        1. <strong id="7actg"></strong>
        2. <table id="7actg"></table>

        3. <address id="7actg"></address>
          <address id="7actg"></address>
          1. <object id="7actg"><tt id="7actg"></tt></object>
            日韩一区二区三区免费观看 | ccyy | 午夜激情视频三区在线 | 日本泡妞xxxxxxxxx78 | 小坏蛋好大好涨爽飞了 | 大学生私密按摩hd | 男女插插动态图 | 免费看AAA片 | 豆花视频精品一区 | 精品操逼|