一般权限控制,是先给角色分配对应权限,然后再给用户分配角色;总权限应该是在代码编写的时候就已经固定了,例如有个用户更新的接口,这里就会诞生一个用户更新的权限,接口在权限就在,没有接口也就没有了这个权限;
所以总权限我是维护在代码中静态常量,在AuthorizeAttribute中设置权限也是要常量;
使用原生AuthorizeAttribute的Policy和用户的Claim(有userClaim和roleClaim),userClaim和roleClaim是AspNetCore.Identity的表,保存用户的声明信息和角色声明信息,也就是可以在roleClaim中保存角色的权限;
用户登录后获取token,token经过ProfileService处理,带有用户的权限Claim,用户请求需要权限的接口时,会检查token中有没有这个权限要求的Claim;
分为两个服务,一个Identityserver4服务,一个UserAPI服务;
Identityserver4服务
ConfigureServices
string mysqlConnectionStrings = $"Data Source={Host};port={Port};Initial Catalog={Database};user id={UserID};password={Password};"; services.AddDbContext<ApplicationDbContext>(options => options.UseMySql(mysqlConnectionStrings)); services.AddIdentity<ApplicationUser, ApplicationRole>() .AddEntityFrameworkStores<ApplicationDbContext>() .AddDefaultTokenProviders(); var builder = services.AddIdentityServer() .AddInMemoryIdentityResources(Config.Ids) .AddInMemoryApiResources(Config.Apis) .AddInMemoryClients(Config.Clients(Configuration)) .AddAspNetIdentity<ApplicationUser>() //.AddResourceOwnerValidator<ResourceOwnerPasswordValidator>() .AddProfileService<ProfileService>(); // not recommended for production - you need to store your key material somewhere secure builder.AddDeveloperSigningCredential();
需要注意ProfileService,这里返回token中Claim,去数据库获取用户的Claim
/// <summary> /// This method is called whenever claims about the user are requested (e.g. during token creation or via the userinfo endpoint) /// /// </summary> /// <param name="context">The context.</param> /// <returns></returns> public virtual async Task GetProfileDataAsync(ProfileDataRequestContext context) { var sub = context.Subject?.GetSubjectId(); //context.Subject.Claims 为登录设置的Claims,此处直接用数据库中的claims,忽略context.Subject.Claims if (sub == null) throw new Exception("No sub claim present"); var user = await _userServer.FindByIdAsync(sub); if (user == null) { Logger?.LogWarning("No user found matching subject Id: {0}", sub); } else {
//获取用户的权限 var userPermissions = await _rolePermissionServer.GetUserPermissions(user.Id); List<Claim> claims = new List<Claim>(); claims = userPermissions.Select(a => new Claim("UserPermission", a.NormalizedName)).ToList(); claims.Add(new Claim("username", user.UserName)); claims.Add(new Claim("name", user.Name)); context.IssuedClaims = claims; } }
UserAPI服务
UserApi服务除了正常的配置Identityserver4服务以外,还需要添加认证需要的Policy,因为权限都需要认证,所以我把所有的权限都加进去;
//获取所有的权限列表
var permissionsList = PermissionNames.GetPermissionsList(); //设置Authorize的policy,可以添加多个 services.AddAuthorization(options => { foreach (var item in permissionsList) { options.AddPolicy(item.NormalizedName, policyAdmin => { policyAdmin.RequireClaim("UserPermission", item.NormalizedName); }); } });
下面两个中间件也不能忘记
app.UseAuthentication();
app.UseAuthorization();
接下来是Controller部分
[HttpGet] [Route("manage")] [Authorize(PermissionNames.UserManage_list)] public async Task<PageBase<UserListItemDto>> UserManageList([FromQuery]UserListRequestDto request) { return await _userServer.UserManageList(request); } [HttpGet] [Route("manage/{id}")] [Authorize(PermissionNames.UserManage_detail)] public async Task<UserDetailDto> UserManageDetail(int id) { return await _userServer.UserManageDetail(id); } [HttpPut] [Route("manage")] [Authorize(PermissionNames.UserManage_update)] public async Task<bool> UserManageUpdate(UserEditDto userEditDto) { return await _userServer.UserManageUpdate(UserId, userEditDto); } [HttpDelete] [Route("manage/{id}")] [Authorize(PermissionNames.UserManage_delete)] public async Task<bool> UserManageDelete(int id) { return await _userServer.UserManageDelete(UserId, id); }
具体代码,还有更多内容学习:https://github.com/zhanghm1/DiuDiuTemplate