一般權限控制,是先給角色分配對應權限,然后再給用戶分配角色;總權限應該是在代碼編寫的時候就已經固定了,例如有個用戶更新的接口,這里就會誕生一個用戶更新的權限,接口在權限就在,沒有接口也就沒有了這個權限;
所以總權限我是維護在代碼中靜態常量,在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