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

虚幻大学 虚幻 407℃ 0评论

Python微信订餐小程序课程视频

https://edu.csdn.net/course/detail/36074

Python实战量化交易理财系统

https://edu.csdn.net/course/detail/35475

目录

  • 模块拆分
  • 代码重构

模块拆分

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

代码重构

  • AuthenticationController
  • PermissionController
  • IAuthorizationMiddlewareResultHandler
  • ISaveChangesInterceptor

AuthenticationController

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

登录

[HttpPost]  
[Route("login")]  
public async Task 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 
 { 
 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 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 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 FindUserPermission(string username)
{
    return Ok(await _userPermission.FindUserPermission(username));
}

[Route("role/{roleName}")]
[HttpGet]
public async Task FindRolePermission(string roleName)
{
    return Ok(await _rolePermission.FindRolePermission(roleName));
}

[Route("addtorole")]
[HttpPost]
public async Task AddToRole([FromQuery] string role, [FromQuery] string permission)
{
    await _rolePermission.AddRolePermission(role, permission);
    return Ok();
}

[Route("addtouser")]
[HttpPost]
public async Task 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();
 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 ValueTaskint>> 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 entities,IEnumerable 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 entities,IEnumerable 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 entities,IEnumerable 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();

 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

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

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

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

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

转载请注明:虚坏叔叔 » .NET 云原生架构师训练营(权限系统 代码重构)–学习笔记

喜欢 (0)

您必须 登录 才能发表评论!