具有角色的授权属性重定向到访问被拒绝

Authorize attribute with role redirects to Access denied

我有一个简单的 ASP .NET MVC 应用程序。 我正在尝试进行身份验证和授权。

为了进行身份验证,我已经设置了 Azure AD,并且正在使用中间件向 HttpContext.User 添加角色。 我知道该角色已添加,因为在 View 上,如果我使用 User.IsInRole("Admin") 进行条件渲染,则 UI 会正确渲染。但是具有 Authorize[Roles = "Admin"] 的控制器将我重定向到

http://localhost:5433/MicrosoftIdentity/Account/AccessDenied?ReturnUrl=%2FHome%2FAdd

我都累了Authorize[Policy = "Admin"]结果还是一样

如果我从服务中删除 AddMicrosoftIdentityUI() 然后它会被重定向到

http://localhost:5433/Account/AccessDenied?ReturnUrl=%2FHome%2FAdd

我不知道授权属性是如何工作的。它不检查User.IsInRole("Admin")吗?请告诉我

另外,对我而言,角色已添加到 UserDetails table 中,我确实拥有 'Admin'

的角色

我也可以编写一个自定义属性并让它工作。我只想知道现在真正的问题是什么。

代码:

Startup.cs

 public class Startup
    {
        public Startup(IConfiguration configuration)
        {
            Configuration = configuration;
        }

        public IConfiguration Configuration { get; }

        public void ConfigureServices(IServiceCollection services)
        {
            services.AddDbContextPool<EmpDBContext>(
            options => options.UseSqlServer(Configuration.GetConnectionString("EmployeeDBConnection")));

            services.AddMicrosoftIdentityWebAppAuthentication(Configuration, "AzureAd");


            services.AddControllersWithViews(options =>
            {
                var policy = new AuthorizationPolicyBuilder()
                 .RequireAuthenticatedUser()
                 .Build();
                options.Filters.Add(new AuthorizeFilter(policy));
            }).AddMicrosoftIdentityUI().AddNewtonsoftJson();

            services.AddAuthorization(options =>
            {
                options.AddPolicy("Admin", policy => policy.RequireRole("Admin"));
                options.AddPolicy("Employee", policy => policy.RequireRole("Employee"));
            });
        }

        public void Configure(IApplicationBuilder app, IWebHostEnvironment env)
        {
            if (env.IsDevelopment())
            {
                app.UseDeveloperExceptionPage();
            }
            else
            {
                app.UseExceptionHandler("/Home/Error");
            }
            app.UseStaticFiles();

            app.UseRouting();

            app.UseAuthentication();
            app.UseAuthorization();

            app.UseAddClaimsFromDb(); // CUSTOM MIDDLEWARE TO ADD ROLE TO USER

            // CHECKING IF ROLE WAS ADDED
            app.Use(async (context, next) =>
            {
                if (context.User != null && context.User.Identity.IsAuthenticated)
                {
                    bool x = context.User.IsInRole("Admin");
                    System.Console.WriteLine(x); // RETURNS TRUE

                }
                await next();
            });
            app.UseEndpoints(endpoints =>
            {
                endpoints.MapControllerRoute(
                    name: "default",
                    pattern: "{controller=Home}/{action=Index}/{id?}");
            });
        }
    }

UseAddClaimsFromDb (AddClaimMiddleware.cs)

public class AddClaimMiddleware
    {
        private readonly RequestDelegate next;

        public AddClaimMiddleware(RequestDelegate next)
        {
            this.next = next;
        }

        public async Task InvokeAsync(HttpContext httpContext, EmpDBContext dBContext)
        {
            var user = httpContext.User;

            // does not perist between request
            if(user != null && user.Identity.IsAuthenticated)
            {
                string email = user.Identity.Name;

                UserDetail userDetail = await dBContext.UserDetails.FirstOrDefaultAsync(e => e.EmailId == email);
                string role = userDetail?.Role ?? "Employee";

                ClaimsIdentity claimsIdentity = new ClaimsIdentity();
                claimsIdentity.AddClaim(new Claim(ClaimTypes.Role, role));

                user.AddIdentity(claimsIdentity);

               // httpContext.Session.SetString("employeeId", userDetail.EmployeeId.ToString());

            }

            await next(httpContext);
        }
    }

我有类似的方法,但我将 Blazor WASM 与 Azure AD B2C 结合使用。

为了将角色声明添加到当前用户的身份中,您需要覆盖现有身份 (ClaimPrincipal) 以便拥有一个 具有所需角色的身份

我使用这个代码:

context.Principal = await claimsTransformation.TransformAsync(context.Principal);
...
 public async Task<ClaimsPrincipal> TransformAsync(ClaimsPrincipal principal)
        {
            // Clone current identity
            var clone = principal.Clone();
            var newIdentity = (ClaimsIdentity)clone.Identity;

            // Support AD and local accounts
            var nameId = principal.Claims.FirstOrDefault(c => c.Type == ClaimTypes.NameIdentifier ||
                                                              c.Type == ClaimTypes.Name);
            if (nameId == null)
            {
                return principal;
            }

            // Get user from database
            var userResponse = await _userService.GetAsync(nameId.Value);
            if (!userResponse.Succeeded || userResponse.Data is null)
            {
                return principal;
            }

            var user = userResponse.Data;
            var rolesResponse = await _userService.GetRolesAsync(user.Id);

            if (rolesResponse.Succeeded && rolesResponse.Data is not null)
            {
                // Add role claims to cloned identity
                foreach (var role in rolesResponse.Data.UserRoles.Where(w => w.Selected).ToList())
                {
                    var claim = new Claim(newIdentity.RoleClaimType, role.RoleName);
                    newIdentity.AddClaim(claim);
                }
            }

            return clone;
        }

我在我的 Toekn 验证中应用了这个转换,但在你的情况下,我认为应该在中间件部分注入。

我通过重新排列中间件顺序修复了上述问题

app.UseAuthentication();
app.UseAddClaimsFromDb();
app.UseAuthorization();

我仍然不知道它起作用的真正原因。如果有人可以更新此内容或添加新答案,那就太好了。