.NET云原生架构师训练营(权限系统代码重构)--学习笔记

博客 动态
0 191
优雅殿下
优雅殿下 2022-02-21 22:55:39
悬赏:0 积分 收藏

.NET 云原生架构师训练营(权限系统 代码重构)--学习笔记

目录

  • 模块拆分
  • 代码重构

模块拆分

代码重构

  • AuthenticationController
  • PermissionController
  • IAuthorizationMiddlewareResultHandler
  • ISaveChangesInterceptor

AuthenticationController

新增 AuthenticationController 用于登录和注册;登录会颁发 jwt token,包含用户的 claims 和 role 的 claims

登录

[HttpPost]  [Route("login")]  public async Task<IActionResult> Login([FromBody] LoginRequest.LoginModel model)  {      var user = await _userManager.FindByNameAsync(model.Username);    var userClaims = await _userManager.GetClaimsAsync(user);    if (user != null && await _userManager.CheckPasswordAsync(user, model.Password))      {          var userRoles = await _userManager.GetRolesAsync(user);          var authClaims = new List<Claim>          {              new Claim(ClaimTypes.Name, user.UserName),              new Claim(JwtRegisteredClaimNames.Jti, Guid.NewGuid().ToString()),          };          foreach (var userRole in userRoles)          {              authClaims.Add(new Claim(ClaimTypes.Role, userRole));            var role = await _roleManager.FindByNameAsync(userRole);            var roleClaims = await _roleManager.GetClaimsAsync(role);            authClaims.AddRange(roleClaims);        }        authClaims.AddRange(userClaims);        var authSigningKey = new SymmetricSecurityKey(Encoding.UTF8.GetBytes(_configuration["JWT:Secret"]));          var token = new JwtSecurityToken(              issuer: _configuration["JWT:ValidIssuer"],              audience: _configuration["JWT:ValidAudience"],              expires: DateTime.Now.AddHours(3),              claims: authClaims,              signingCredentials: new SigningCredentials(authSigningKey, SecurityAlgorithms.HmacSha256)          );          return Ok(new          {              token = new JwtSecurityTokenHandler().WriteToken(token),              expiration = token.ValidTo          });      }      return Unauthorized();  }  

注册

[HttpPost]  [Route("register")]  public async Task<IActionResult> Register([FromBody] RegisterRequest model)  {      var userExists = await _userManager.FindByNameAsync(model.Username);      if (userExists != null)          return StatusCode(StatusCodes.Status500InternalServerError, "User already exist");      var user = new IdentityUser()      {          Email = model.Email,          SecurityStamp = Guid.NewGuid().ToString(),          UserName = model.Username      };      var result = await _userManager.CreateAsync(user, model.Password);      if (!result.Succeeded)          return StatusCode(StatusCodes.Status500InternalServerError, result.Errors);      return Ok();  }  

PermissionController

PermissionController 新增 创建实体权限,用户角色相关接口

[Route("entity")][HttpPost]public async Task<IActionResult> CreateEntityPermission([FromBody] CreateEntityPermissionRequest request){    var permission = new Permission()    {        Data = request.Data,        Description = request.Description,        DisplayName = request.DisplayName,        Key = request.Key,        Group = request.Group,        Resources = request.resources.Select(r => new EntityResource() { Key = r })    };    await _permissionManager.CreateAsync(permission);    return Ok();}[Route("user/{username}")][HttpGet]public async Task<IActionResult> FindUserPermission(string username){    return Ok(await _userPermission.FindUserPermission(username));}[Route("role/{roleName}")][HttpGet]public async Task<IActionResult> FindRolePermission(string roleName){    return Ok(await _rolePermission.FindRolePermission(roleName));}[Route("addtorole")][HttpPost]public async Task<IActionResult> AddToRole([FromQuery] string role, [FromQuery] string permission){    await _rolePermission.AddRolePermission(role, permission);    return Ok();}[Route("addtouser")][HttpPost]public async Task<IActionResult> AddToUser([FromQuery] string username, [FromQuery] string permission){    await _userPermission.AddUserPermission(username, permission);    return Ok();}

IAuthorizationMiddlewareResultHandler

在 asp .net core 3.1 之后 ActionAuthorizationFilter 已经没用了,需要使用 IAuthorizationMiddlewareResultHandler

IAuthorizationMiddlewareResultHandler 是授权管理里面的一个 Handler,可以从 endpoint 的 Metadata 获取到 ControllerActionDescriptor,从而获取到 permissionKey

authorizeResult.Challenged:未登录返回401

authorizeResult.Forbidden:未授权返回403

需要将 Challenged 放在 Forbidden 之前,不然未登录也返回 403

public class DotNetNBAuthorizationMiddlewareResultHandler : IAuthorizationMiddlewareResultHandler{    public async Task HandleAsync(RequestDelegate next, HttpContext context, AuthorizationPolicy policy,        PolicyAuthorizationResult authorizeResult)    {        var endpoint = context.GetEndpoint();        var actionDescriptor = endpoint.Metadata.GetMetadata<ControllerActionDescriptor>();        if (actionDescriptor != null)        {            var permissions = context.User.Claims.Where(c => c.Type == Core.ClaimsTypes.Permission);            var permissionKey = actionDescriptor.GetPermissionKey();            var values = permissions.Select(p => p.Value);            if (!values.Contains(permissionKey))            {                await context.ForbidAsync();                return;            }        }                if (authorizeResult.Challenged)        {            if (policy.AuthenticationSchemes.Count > 0)            {                foreach (var scheme in policy.AuthenticationSchemes)                {                    await context.ChallengeAsync(scheme);                }            }            else            {                await context.ChallengeAsync();            }            return;        }        else if (authorizeResult.Forbidden)        {            if (policy.AuthenticationSchemes.Count > 0)            {                foreach (var scheme in policy.AuthenticationSchemes)                {                    await context.ForbidAsync(scheme);                }            }            else            {                await context.ForbidAsync();            }            return;        }                await next(context);    }}

ISaveChangesInterceptor

ISaveChangesInterceptor 是 entity 操作的拦截器,获取 Added,Deleted,Modified 三种状态的实体

新增和删除分别获取对应的 permission key 与用户的 permission 对比

更新需要遍历获取每个实体更新的 member 的 permission key 与用户的 permission 对比

public class SavingChangeInterceptor: ISaveChangesInterceptor{    ...    public async ValueTask<InterceptionResult<int>> SavingChangesAsync(DbContextEventData eventData, InterceptionResult<int> result,        CancellationToken cancellationToken = new CancellationToken())    {        var contextName = eventData.Context.GetType().Name;        var permissions = await _permissionManager.GetByGroupAsync(contextName);        var entityPermissions = permissions.Select(p => new EntityPermission(p)).ToList();                if (permissions==null || !permissions.Any())            return result;        var addedEntities = eventData.Context.ChangeTracker.Entries().Where(e => e.State == EntityState.Added);        var deletedEntties = eventData.Context.ChangeTracker.Entries().Where(e => e.State == EntityState.Deleted);        var modifiedEntities = eventData.Context.ChangeTracker.Entries().Where(e => e.State == EntityState.Modified);        await CheckAddedEntitiesAsync(addedEntities, entityPermissions);        await CheckDeletedEntitiesAsync(deletedEntties,entityPermissions);        await CheckModifiedEntitiesAsync(modifiedEntities,entityPermissions);                return result;    }        private async Task CheckAddedEntitiesAsync(IEnumerable<EntityEntry> entities,IEnumerable<EntityPermission> permissions)    {        foreach (var entity in entities)        {            var entityName = entity.Metadata.Name;            var entityPermissions = permissions.Where(p => p.Data.EntityName == entityName);                        if(!entityPermissions.Any())                continue;            var user = _contextAccessor.HttpContext.User;            if (!user.Identity.IsAuthenticated)                throw new AuthenticationException();            var createPermission = entityPermissions.Where(e => e.Data.Create).Select(e => e.Key);            var claimValues = user.Claims.Where(c => c.Type == ClaimsTypes.Permission).Select(c => c.Value);            if (!createPermission.Intersect(claimValues).Any())                throw new AuthorizationException();        }    }    private async Task CheckDeletedEntitiesAsync(IEnumerable<EntityEntry> entities,IEnumerable<EntityPermission> permissions)    {        foreach (var entity in entities)        {            var entityName = entity.Metadata.Name;            var entityPermissions = permissions.Where(p => p.Data.EntityName == entityName);                        if(!entityPermissions.Any())                continue;            var user = _contextAccessor.HttpContext.User;            if (!user.Identity.IsAuthenticated)                throw new AuthenticationException();            var deletePermission = entityPermissions.Where(e => e.Data.Delete).Select(e => e.Key);            var claimValues = user.Claims.Where(c => c.Type == ClaimsTypes.Permission).Select(c => c.Value);            if (!deletePermission.Intersect(claimValues).Any())                throw new AuthorizationException();        }    }    private async Task CheckModifiedEntitiesAsync(IEnumerable<EntityEntry> entities,IEnumerable<EntityPermission> permissions)    {        foreach (var entity in entities)        {            var entityName = entity.Metadata.Name;            var entityPermissions = permissions.Where(p => p.Data.EntityName == entityName);                        if(!entityPermissions.Any())                continue;            var user = _contextAccessor.HttpContext.User;            if (!user.Identity.IsAuthenticated)                throw new AuthenticationException();                        var modifiedMembers = entity.Members.Where(m => m.IsModified).Select(m => m.Metadata.Name);            var canUpdatePermissionKeys = new List<string>();            foreach (var permission in entityPermissions)            {                var definedMembers = permission.Data.Members.Where(m => m.Update && modifiedMembers.Contains(m.MemberName));                if(definedMembers.Any())                    canUpdatePermissionKeys.Add((permission.Key));            }            var claimValues = user.Claims.Where(c => c.Type == ClaimsTypes.Permission).Select(c => c.Value);            if (!canUpdatePermissionKeys.Intersect(claimValues).Any())                throw new AuthorizationException();        }    }        ...}

GitHub源码链接:

https://github.com/MingsonZheng/dotnetnb.security refactor 分支

课程链接

https://appsqsyiqlk5791.h5.xiaoeknow.com/v1/course/video/v_5f39bdb8e4b01187873136cf?type=2

知识共享许可协议

本作品采用知识共享署名-非商业性使用-相同方式共享 4.0 国际许可协议进行许可。

欢迎转载、使用、重新发布,但务必保留文章署名 郑子铭 (包含链接: http://www.cnblogs.com/MingsonZheng/ ),不得用于商业目的,基于本文修改后的作品务必以相同的许可发布。

如有任何疑问,请与我联系 (MingsonZheng@outlook.com) 。

posted @ 2022-02-21 22:05 MingsonZheng 阅读(1) 评论(0) 编辑 收藏 举报
回帖
    优雅殿下

    优雅殿下 (王者 段位)

    2018 积分 (2)粉丝 (47)源码

    小小码农,大大世界

     

    温馨提示

    亦奇源码

    最新会员