Blazor Server 和 IClaimsTransformation 的使用

问题描述 投票:0回答:1

Net Core 和我一直在尝试创建一个小型 Blazor Server 应用程序,以期将一些较大的内部应用程序(当前在 .Net WebForms 中)迁移到其中。我们混合使用 Windows 身份验证(用户只有在我们的域中才能访问应用程序)和来自数据库中 UserRoles 表的角色。 我已成功创建一个 Blazor 服务器应用程序,该应用程序使用 Windows 身份验证并使用 IClaimsTransformation 类从数据库中获取用户的角色并将其添加为声明。一切似乎都运行良好,我可以使用这些角色来授权内容。 我对其工作原理的理解是,每次应用程序需要应用授权规则时,它最终都会通过 IClaimsTransformation 进行调用,而 IClaimsTransformation 又会调用我的方法来查询数据库中的用户角色。

但是,在调试我的应用程序时,我注意到我的 IClaimsTransformation 类中的方法在启动应用程序时被多次调用(每次都调用数据库),但随后再也不会被调用。当我进入应用程序中具有授权规则的部分时,它们会被强制执行,所以我认为默认情况下会缓存角色? (我没有设置任何缓存)

这意味着,如果用户在使用应用程序时角色发生更改,则应用程序中持有的声明不会更新。强制新角色生效的唯一方法是关闭并重新打开应用程序,这会导致 IClaimsTransformation 类中的方法被多次调用并获取新角色。

真的,我只是好奇我所看到的这种行为是否是我的场景所预期的,或者我是否在配置中遗漏了某些内容。

我的 IClaimsTransformation 课程:

public class ClaimsTransformer : IClaimsTransformation
    {
        private readonly UserProfileService _userProfileService;
       
        public ClaimsTransformer(UserProfileService userProfileService) 
        {
            _userProfileService = userProfileService;
        }

        public async Task<ClaimsPrincipal> TransformAsync(ClaimsPrincipal principal)
        {
            // User is not authenticated so just return right away
            if (principal.Identity?.IsAuthenticated is false)
            {
                return principal;
            }

           
           ClaimsIdentity claimsIdentity = new ClaimsIdentity(); 

            //below calls method in userProfileService that returns list of roles for the given username 
            List<Role> roleList = (await _userProfileService.GetRolesForUserNameAsync(principal.Identity.Name.ToList();

            foreach (Role r in roleList) {
                if (!principal.HasClaim(claim => claim.Type == r.RoleName))
                {
                    claimsIdentity.AddClaim(new Claim(claimsIdentity.RoleClaimType, r.RoleName));
                }                
            }
            principal.AddIdentity(claimsIdentity);
            return principal;
        }

Program.cs的相关部分:

builder.Services.AddAuthentication(NegotiateDefaults.AuthenticationScheme) 
   .AddNegotiate();

builder.Services.AddAuthorization(options =>
{
    // By default, all incoming requests will be authorized according to the default policy.
    options.FallbackPolicy = options.DefaultPolicy;    
}); 

builder.Services.AddTransient<IClaimsTransformation, ClaimsTransformer>();

授权页面中使用的视图:

<AuthorizeView Roles="Admin">
    <Authorized>
        <p>You are an Admin</p>
    </Authorized>
    <NotAuthorized>
        <p>You are not an Admin</p>
    </NotAuthorized>    
</AuthorizeView>
asp.net-core blazor blazor-server-side
1个回答
0
投票

这种行为是可以预料的。

而是执行如下所示的操作,

public class ApplicationAuthenticationStateProvider : AuthenticationStateProvider
{
    private readonly AuthenticationStateProvider _authenticationStateProvider;
    private readonly IUserService _userService;

    public ApplicationAuthenticationStateProvider(AuthenticationStateProvider AuthenticationStateProvider, IUserService userService)
    {
        _authenticationStateProvider = AuthenticationStateProvider;
        _userService = userService;
    }

    public override async Task<AuthenticationState> GetAuthenticationStateAsync()
    {
        AuthenticationState authenticationState = await _authenticationStateProvider.GetAuthenticationStateAsync();
        if (authenticationState.User.Identity?.IsAuthenticated != true)
        {
            return authenticationState;
        }

        // Check if user already has the UserId claim
        if (authenticationState.User.HasClaim(x => x.Type == ApplicationClaimTypes.UserId))
        {
            return authenticationState;
        }
        
        // TODO: Get user from database using IUserService and current claims
        string applicationUserId = "someUserId"

        ClaimsIdentity claimsIdentity = new ClaimsIdentity();
        claimsIdentity.AddClaim(new Claim(ApplicationClaimTypes.UserId, applicationUserId));

        authenticationState.User.AddIdentity(claimsIdentity);

        return authenticationState;
    }
}

注册

ApplicationAuthenticationStateProvider
,

builder.Services.AddScoped<ApplicationAuthenticationStateProvider>();

使用,

// This is some service where you want to use authenticationState
public class BlazorIdentityService : IIdentityService
{
    private readonly ApplicationAuthenticationStateProvider _applicationAuthenticationStateProvider;

    public BlazorIdentityService(ApplicationAuthenticationStateProvider applicationAuthenticationStateProvider)
    {
        _applicationAuthenticationStateProvider = applicationAuthenticationStateProvider;
    }

    public async Task<string> GetCurrentUserId()
    {
        AuthenticationState authenticationState = await _applicationAuthenticationStateProvider?.GetAuthenticationStateAsync();

        return authenticationState.User?.Claims?.SingleOrDefault(x => x.Type == ApplicationClaimTypes.UserId)?.Value;
    }
}
© www.soinside.com 2019 - 2024. All rights reserved.