具有角色的授权属性重定向到访问被拒绝
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();
我仍然不知道它起作用的真正原因。如果有人可以更新此内容或添加新答案,那就太好了。
我有一个简单的 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();
我仍然不知道它起作用的真正原因。如果有人可以更新此内容或添加新答案,那就太好了。