92 Star 290 Fork 154

风起兮 / NetCoreFast

加入 Gitee
与超过 1200万 开发者一起发现、参与优秀开源项目,私有仓库也完全免费 :)
免费加入
克隆/下载
Authority.md 9.62 KB
一键复制 编辑 原始数据 按行查看 历史
风起兮 提交于 2023-02-12 20:57 . 解决项目层级问题

权限控制

有两种权限控制方案:

1、基于角色-权限-资源的权限控制(较细粒度)
2、基于角色策略的权限控制(粗粒度)

一、基于角色-权限-资源的权限控制(较细粒度)

第1步、后台添加资源-权限-角色(推荐)

1、添加资源

初始数据库的时候,框架已经初始了部分资源,在Web.DB.InitDatas类。你可以在管理后台UI界面添加资源或者Web.DB.InitDatabase.cs类初始资源,此类不会自动生成覆盖,更新项目的时候也不会被覆盖

var ress = new Res[]
   {
   ……
   new Res {Name="地区列表",Value="Sys_Area_List"},
   new Res {Name="地区详情",Value="Sys_Area_Get"},
   new Res {Name="地区添加",Value="Sys_Area_Add"},
   new Res {Name="地区修改",Value="Sys_Area_Update"},
   new Res {Name="地区删除",Value="Sys_Area_Delete"},
   new Res {Name="地区批量删除",Value="Sys_Area_BatchDelete"},
   ……
   };
context.Ress.AddRange(ress);
注意:
①Name资源描述
②Value资源值。组成:模块名称_实体类名称_action方法。权限控制会根据角色拥有的权限,
权限拥有的资源,判断某个用户是否拥有访问某个资源的能力。资源值不区分大小写!!!

2、添加权限,给权限赋予访问的资源

建议通过管理后台UI界面添加权限,并给权限赋予访问的资源值

//添加权限
Authority authority = new Authority() { Name = "图片管理", Code = "image_manger" };
authorityBll.Add(authority);
//赋予权限资源
Res res = resBll.SelectOne(o => o.Id == 3);
AuthorityRes authorityRes = new AuthorityRes() { Authority = authority,Res= res };
authorityResBll.Add(authorityRes);

3、添加角色,给角色赋予访问的权限

建议通过管理后台UI界面添加角色,并给角色赋予访问的权限

//添加角色
Role role = new Role() { Name = "管理员", Code = "manger" };
roleBll.Add(role);
//查询权限
Authority authority = authorityBll.SelectOne(o => o.Id == 2);
//添加角色权限
RoleAuthority roleAuthority = new RoleAuthority() {Role=role,Authority=authority };
roleAuthorityBll.Add(roleAuthority);

第2步、框架启动,自动加载角色及拥有的资源集合

此步骤框架自动加载,无须人工干预
Web.Security.AuthorityLoadWebApp实现了Web.Util.IWebApp接口,框架在启动的时候,自动调用此类的OnStarted()方法,加载数据库所有角色及对应的资源到缓存。

 var roles = roleBll.SelectAll();
 foreach (var r in roles)
{
//角色对应资源序号索引
List<int> ressindex = new List<int>();
 //角色对应权限
var roleAuthorites = roleAuthorityBll.SelectAll(o => o.RoleId == r.Id);
foreach (var a in roleAuthorites)
   {
   
    //权限对应资源
    var authorityRess = authorityResBll.SelectAll(o => o.AuthorityId == a.AuthorityId);
    foreach (var _authorityRes in authorityRess)
         {
            int idx = roleRess.GetResIndex(_authorityRes.Res.Value);
            if (idx != -1)//资源存在添加到角色资源列表
            {
                ressindex.Add(idx);
            }
         }
    }
     //保存对应角色和资源下标索引
      roleRess.AddRoleRess(r.Code, ressindex);
}
cache.Set(RoleResMap.CACHE_KEY, roleRess);

第3步、资源权限控制

  • 判断是否有访问某个资源的权限
    控制器类或操作方法action添加**[Authority(Module ="Sys")]**注解,如:
[Route("api/[controller]/[action]")]  
[ApiController]  
[Authority(Module ="Sys")] //加在控制器,控制器内所有方法需登录且具有访问对应资源的权限        
public class AreaController : MyBaseController  
{
    IAreaBll bll{geet;set;};  
    [HttpGet]  
    public Result List([FromQuery] Dictionary<string, string> where)  
    {  
        return Result.Success("succeed").SetData(bll.Query(where));  
    }      
}  
[Route("api/[controller]/[action]")]  
[ApiController]  	  	 
public class AreaController : MyBaseController  
{  
	IAreaBll bll{geet;set;};  
	[HttpGet]    
	[Authority(Module ="Sys")] //加在操作方法,某个方法需登录且具有访问对应资源的权限              
	public Result List([FromQuery] Dictionary<string, string> where)  
	{   
		return Result.Success("succeed").SetData(bll.Query(where));  
	}      
}  
注意:
①Module模块名称与添加资源时的模块名称保持一致,比如Value="Sys_Area_List",因为Authority过滤器当用户在访问
某个方法资源时,会自动获取当前控制器名和方法名称,通过"模块名称_控制器名称_方法名称"构成完整的资源值。如果添加资源的时候,资源值
没有模块名称,Module不需要赋值	
②当角色为超级管理员admin的时候,拥有所有权限,即不需要验证权限
  • 登录就能访问该资源的权限
    控制器类或操作方法action添加**[Authority(Access = AccessType.Login)]**注解,表明用户只要登录就能访问该资源,如:
[HttpGet("getuserinfo")]
[Authority(Access = AccessType.Login)]
public Result<User> GetUserinfo()
{
    User obj = null;
    obj = this.userBll.SelectOne(MyUser.Id);
    if (obj != null)
    {
        obj.Pswd = "";
        return Result<User>.Success("获取成功").SetData(obj);
    }
    return Result<User>.Error("获取失败").SetData(new User() { });
} 

二、基于角色策略的权限控制(粗粒度)

  • 登录就能访问
    控制器类或操作方法action添加**[Authorize()]**注解,表示需要经过登录才能访问,而不管登录的角色
[Route("api/[controller]/[action]")]  
[ApiController]  
[Authorize()] //加在控制器,控制器内所有方法需登录访问        
public class AreaController : MyBaseController  
{
    IAreaBll bll{geet;set;};  
    [HttpGet]  
    public Result List([FromQuery] Dictionary<string, string> where)  
    {  
        return Result.Success("succeed").SetData(bll.Query(where));  
    }      
}  
[Route("api/[controller]/[action]")]  
	[ApiController]  	  	 
	public class AreaController : MyBaseController  
	{  
		IAreaBll bll{geet;set;};  
		[HttpGet]    
		[Authorize()] //加在操作方法,某个方法需登录访问            
		public Result List([FromQuery] Dictionary<string, string> where)  
		{   
		    return Result.Success("succeed").SetData(bll.Query(where));  
		 }      
	}  
  • 指定策略才能访问
    控制器类或操作方法action添加**[Authorize("策略名称")]**注解,表示需要某个策略才能访问。

内置策略名称有:admin策略对应角色admin,user策略对应角色user

     //加在控制器,控制器内所有方法需登录访问   
    [Route("api/[controller]/[action]")]   
    [ApiController]   
    [Authorize("admin")]    //策略为admin才能访问   
    public class AreaController : MyBaseController   
    {   
        IAreaBll bll{geet;set;};    
        [HttpGet]   
        public Result List([FromQuery] Dictionary<string, string> where)   
        {   
            return Result.Success("succeed").SetData(bll.Query(where));   
        }           
    }   

注:如何自定义策略名称
1.策略名称在Web/Jwt/JwtExtension.cs文件定义注册。一个策略名称对应一个或多个__角色__
#region 授权

      services.AddAuthorization(options =>        
      {      
		//AddPolicy("admin" 自定义策略名称         
		//policy => policy.RequireRole("admin")策略要求角色,一个策略可以有多个角色    
        options.AddPolicy("admin", policy => policy.RequireRole("admin").Build());    
        options.AddPolicy("teacher", policy => policy.RequireRole("teacher","admin").Build());    
        options.AddPolicy("student", policy => policy.RequireRole("student").Build());    
      });    
      #endregion 

2.角色名称与登录时候生成token的角色名称保持一致。在Web/Controllers/DefaultController.cs登录方法生成__token__

    public class DefaultController : MyBaseController     
      {              
        [HttpPost("login")]             
        public Result Login(Userinfo o)     
        //public Result Login([FromForm]string username, [FromForm]string password,[FromForm]string type="student")            
        {      
            ……          
            if (obj != null)        
            {       
                type = o.Type;       
                var t = new Token() { Uid = obj.Id, Uname = obj.Username, Role = obj.Role, Type = type, TokenType = TokenType.App, Project = project,ClasssId= classid,SchoolId = schoolid };           
                return Result.Success("登录成功")          
                    .SetData(new Userinfo() { Id = obj.Id, Username = obj.Username, Avatar = obj.Photo, Role = obj.Role, Type = o.Type, Realname = obj.Realname, Tel = obj.Tel, Email = obj.Email, Birthday = obj.Birthday, Token = JwtHelper.IssueJWT(t, this.jwtConfig) });                            
            }      
            return Result.Error("登录失败,用户名密码错误").SetData(new Userinfo() { Token = "" });     
        }    
	  }  

这里Role = obj.Role就是生成token的角色名称。token生成后传递给客户端,客户端每次请求服务端api时携带这个token,然后服务端收到token并解析出角色。
而服务端控制器或方法的**[Authorize("admin")]**注解根据策略名称在上面注册的策略查询到策略对应的角色,与客户端所带token的角色对比,看控制器或方法所需的角色是否包含token的角色, 如果包含表示有权限,反之,无权限。

C#
1
https://gitee.com/qinyongcheng/NetCoreFast.git
git@gitee.com:qinyongcheng/NetCoreFast.git
qinyongcheng
NetCoreFast
NetCoreFast
NET6

搜索帮助