Joh*_*ild 5 c# authorization windows-authentication asp.net-core blazor
我正在尝试在我的 Blazor 服务器应用程序中提供自定义角色。使用 Windows 身份验证进行身份验证的用户应根据其 Active Directory 组获得这些自定义角色之一,一组代表一个角色。
如果用户在正确的组中,则用户将获得 RoleClaimType 类型的声明。这些声明稍后用于授权某些页面和操作。
我还没有看到任何人对使用 Blazor Server 的 Windows 身份验证和 Active Directory 进行过多讨论,因此我遇到了这些问题。这是我的尝试,但它混合了这里和那里的部分。所以我不确定这是最好的方法还是不安全。
这是我到目前为止想出的..
ClaimTransformer.cs,我从 appsettings.json 获得了广告组。
public class ClaimsTransformer : IClaimsTransformation
{
private readonly IConfiguration _configuration;
public ClaimsTransformer(IConfiguration configuration)
{
_configuration = configuration;
}
public Task<ClaimsPrincipal> TransformAsync(ClaimsPrincipal principal)
{
var claimsIdentity = (ClaimsIdentity)principal.Identity
string adGroup = _configuration.GetSection("Roles")
.GetSection("CustomRole")
.GetSection("AdGroup").Value;
if (principal.IsInRole(adGroup))
{
Claim customRoleClaim = new Claim(claimsIdentity.RoleClaimType, "CustomRole");
claimsIdentity.AddClaim(customRoleClaim);
}
return Task.FromResult(principal);
}
}
Run Code Online (Sandbox Code Playgroud)
要让 Claimstransformer 使用 Authorize 属性,请在 Startup.cs 中使用它:
public void Configure(IApplicationBuilder app, IWebHostEnvironment env)
{
...
app.UseAuthorization();
app.UseAuthentication();
...
}
Run Code Online (Sandbox Code Playgroud)
我还在 Startup.cs 中注册了 ClaimsTransformer:
services.AddScoped<IClaimsTransformation, ClaimsTransformer>();
授权整个 Blazor 组件:
@attribute [Authorize(Roles = "CustomRole")]
Run Code Online (Sandbox Code Playgroud)
或授权部分组件:
<AuthorizeView Roles="CustomRole">
<Authorized>You are authorized</Authorized>
</AuthorizeView>
Run Code Online (Sandbox Code Playgroud)
所以我的问题基本上是:
- 这些声明是否必须重新申请?如果到期,他们什么时候到期?
- 这种授权的最佳做法是什么?
- 这种方式安全吗?
你的问题有点老了,我假设你已经找到了一个解决方案,无论如何,也许还有其他人希望在 Windows 身份验证中实现客户角色,所以我发现的简单方法是这样的:
AuthenticationStateProvider然后您可以在服务或组件中注入
var authState = await authenticationStateProvider.GetAuthenticationStateAsync();
var user = authState.User;
var userClaims = new ClaimsIdentity(new List<Claim>()
{
new Claim(ClaimTypes.Role,"Admin")
});
user.AddIdentity(userClaims);
Run Code Online (Sandbox Code Playgroud)
通过这种方式,您可以设置新的角色。
当然,您可以实现自定义逻辑来为每个用户动态添加角色。
这就是我最终根据 AD 组添加角色的方式:
public async void GetUserAD()
{
var auth = await authenticationStateProvider.GetAuthenticationStateAsync();
var user = (System.Security.Principal.WindowsPrincipal)auth.User;
using PrincipalContext pc = new PrincipalContext(ContextType.Domain);
UserPrincipal up = UserPrincipal.FindByIdentity(pc, user.Identity.Name);
FirstName = up.GivenName;
LastName = up.Surname;
UserEmail = up.EmailAddress;
LastLogon = up.LastLogon;
FixPhone = up.VoiceTelephoneNumber;
UserDisplayName = up.DisplayName;
JobTitle = up.Description;
DirectoryEntry directoryEntry = up.GetUnderlyingObject() as DirectoryEntry;
Department = directoryEntry.Properties["department"]?.Value as string;
MobilePhone = directoryEntry.Properties["mobile"]?.Value as string;
MemberOf = directoryEntry.Properties["memberof"]?.OfType<string>()?.ToList();
if(MemberOf.Any(x=>x.Contains("management-team") && x.Contains("OU=Distribution-Groups")))
{
var userClaims = new ClaimsIdentity(new List<Claim>()
{
new Claim(ClaimTypes.Role,"Big-Boss")
});
user.AddIdentity(userClaims);
}
}
Run Code Online (Sandbox Code Playgroud)
编辑
您可以在下面找到我如何加载用户信息和分配角色的示例
using Microsoft.AspNetCore.Components.Authorization;
using Microsoft.EntityFrameworkCore;
using System.DirectoryServices;
using System.DirectoryServices.AccountManagement;
using System.Linq;
using System.Security.Claims;
using System.Threading.Tasks;
public class UserService : IUserService
{
private readonly AuthenticationStateProvider authenticationStateProvider;
private readonly ApplicationDbContext context;
public ApplicationUser CurrentUser { get; private set; }
public UserService(AuthenticationStateProvider authenticationStateProvider, ApplicationDbContext context)
{
this.authenticationStateProvider = authenticationStateProvider;
this.context = context;
}
public async Task LoadCurrentUserInfoAsync()
{
var authState = await authenticationStateProvider.GetAuthenticationStateAsync();
using PrincipalContext principalContext = new PrincipalContext(ContextType.Domain);
UserPrincipal userPrincipal = UserPrincipal.FindByIdentity(principalContext, authState.User.Identity.Name);
DirectoryEntry directoryEntry = userPrincipal.GetUnderlyingObject() as DirectoryEntry;
CurrentUser.UserName = userPrincipal.SamAccountName;
CurrentUser.FirstName = userPrincipal.GivenName;
CurrentUser.LastName = userPrincipal.Surname;
CurrentUser.Email = userPrincipal.EmailAddress;
CurrentUser.FixPhone = userPrincipal.VoiceTelephoneNumber;
CurrentUser.DisplayName = userPrincipal.DisplayName;
CurrentUser.JobTitle = userPrincipal.Description;
CurrentUser.Department = directoryEntry.Properties["department"]?.Value as string;
CurrentUser.MobilePhone = directoryEntry.Properties["mobile"]?.Value as string;
//get user roles from Database
var roles = context.UserRole
.Include(a => a.User)
.Include(a => a.Role)
.Where(a => a.User.UserName == CurrentUser.UserName)
.Select(a => a.Role.Name.ToLower())
.ToList();
var claimsIdentity = authState.User.Identity as ClaimsIdentity;
//add custom roles from DataBase
foreach (var role in roles)
{
var claim = new Claim(claimsIdentity.RoleClaimType, role);
claimsIdentity.AddClaim(claim);
}
//add other types of claims
var claimFullName = new Claim("fullname", CurrentUser.DisplayName);
var claimEmail = new Claim("email", CurrentUser.Email);
claimsIdentity.AddClaim(claimFullName);
claimsIdentity.AddClaim(claimEmail);
}
}
Run Code Online (Sandbox Code Playgroud)
小智 2
我采用了与您类似的方法,但我在范围服务中创建了一个私有的 ClaimsPrincipal 对象来存储添加的策略,因为我发现每次 TransformAsync 调用后更改都丢失了。然后,我添加了一个简单的 UserInfo 类来获取经过身份验证的用户所属的所有组。
这些主张是否必须重新适用?如果过期,什么时候过期?
据我所知,每次调用 AuthenticateAsync 时都必须重新应用声明。我不确定它们是否过期,但我认为 Blazor Server 可能会在向客户端发送新的差异之前运行 TransformAsync,这样就不会被注意到。
此类授权的最佳实践是什么?
不知道,但只要您使用 Blazor Server,内置的身份验证和授权中间件可能是最好的方法之一。不过 WASM 会是一个不同的故事......
这种方式安全吗?
我认为安全问题最终会更多地集中在 Web 服务器上,而不是分配角色的方式上。总的来说,它应该是相对安全的,我认为最大的安全问题取决于诸如
用户授权服务:
public class UserAuthorizationService : IClaimsTransformation {
public UserInfo userInfo;
private ClaimsPrincipal CustomClaimsPrincipal;
public Task<ClaimsPrincipal> TransformAsync(ClaimsPrincipal principal) {
//Creates UserInfo Object on the first Call Only
if (userInfo == null)
userInfo = new UserInfo((principal.Identity as WindowsIdentity).Owner.Value); //Owner.Value Stores SID On Smart Card
//Establishes CustomClaimsPrincipal on first Call
if (CustomClaimsPrincipal == null) {
CustomClaimsPrincipal = principal;
var claimsIdentity = new ClaimsIdentity();
//Loop through AD Group list and applies policies
foreach (var group in userInfo.ADGroups) {
switch (group) {
case "Example AD Group Name":
claimsIdentity.AddClaim(new Claim("ExampleClaim", "Test"));
break;
}
}
CustomClaimsPrincipal.AddIdentity(claimsIdentity);
}
return Task.FromResult(CustomClaimsPrincipal);
}
}
Run Code Online (Sandbox Code Playgroud)
用户信息:
public class UserInfo {
private DirectoryEntry User { get; set; }
public List<string> ADGroups { get; set; }
public UserInfo(string SID) {
ADGroups = new List<string>();
//Retrieve Current User with SID pulled from Smart Card
using (DirectorySearcher comps = new DirectorySearcher(new DirectoryEntry("LDAP String For AD"))) {
comps.Filter = "(&(objectClass=user)(objectSID=" + SID + "))";
User = comps.FindOne().GetDirectoryEntry();
}
//Load List with AD Group Names
foreach (object group in User.Properties["memberOf"])
ADGroups.Add(group.ToString()[3..].Split(",OU=")[0]);
}
}
Run Code Online (Sandbox Code Playgroud)
| 归档时间: |
|
| 查看次数: |
4730 次 |
| 最近记录: |