授权策略总是返回 403(禁止)-MVC/API
Posted
技术标签:
【中文标题】授权策略总是返回 403(禁止)-MVC/API【英文标题】:Authorize Policy always return 403 (Forbidden) - MVC/API 【发布时间】:2018-10-24 01:56:03 【问题描述】:我创建了一个 API(带有 EF Core 的 .Net Core 2),它带有一个端点来检索某些角色。我将 ASPNetIdentity 集成到我的项目中,并且正在使用 AspNetRoles 和 AspNetRoleClaims。
在我的情况下,当调用 API 时,用户具有特定的角色(管理员)。此角色有一些角色声明。在 startup.cs 我为这个角色添加了政策:
options.AddPolicy(
"Create Roles", policy => policy.RequireClaim("Can create roles", "role.create"));
options.AddPolicy(
"View Roles", policy => policy.RequireClaim("Can read roles", "role.read"));
options.AddPolicy(
"Edit Roles", policy => policy.RequireClaim("Can update roles", "role.update"));
options.AddPolicy(
"Delete Roles", policy => policy.RequireClaim("Can delete roles", "role.delete"));
在我的前端中,用户可以使用他们的 Microsoft (azure) 帐户登录,并且他们的 oidc 声明 (ID) 与 AspNetUser 表中的 ID 匹配,如果在用户表中找不到他们的 oidc,则会自动添加他们(使用他们的oidc id 作为 aspnetuser id),他们获得一个默认角色。
但是,当调用 Role 端点时,它总是返回 403 错误(禁止)。当我检查表时,用户具有访问端点的正确角色和角色声明。它怎么可能一直返回403?
端点如下所示:
[HttpGet]
[Authorize(Policy = "View Roles")]
public IEnumerable<IdentityRole> GetRole()
return _context.Roles;
经过一些研究,我发现一篇文章告诉您需要在发送到 API 的令牌中包含用户的角色(声明),但这意味着我需要一个 GET 端点,该端点首先返回用户的角色,前端需要将其拾取并将其添加到令牌中,然后使用令牌中包含的角色调用所有其他端点?还是我在这里走错了路?
----更新----
我 90% 确定策略/授权检查需要将角色声明包含在用户的令牌中。然而,现在的过程如下:
-
用户进入前端项目(react frontend)。
前端使用 adal.js 来检查用户是否通过身份验证,如果他没有通过身份验证,则用户将被重定向到 Microsoft 登录页面。
登录成功后,正在调用API。
在 API 的 DI (AddJwtBearer) 中,oid 声明正在与 AspNetUsers 表的 ID 进行比较,如果不存在,则使用 oid 的 ID 值将用户添加到 AspNetUser 表中AspNet 用户。
现在用户也添加到了 AspNetUser 表中,我可以使用 Asp.Net Identity 进行使用 Roles 和 RoleClaims 的授权。
但问题是 API 最初接收到的令牌是 Azure 令牌,它对我的身份表一无所知(如果我错了,请纠正我)。我相信这也是我的政策不起作用的原因(再次,如果我错了,请纠正我)。
我发现了一个问题或多或少相同的帖子 (https://joonasw.net/view/adding-custom-claims-aspnet-core-2),诀窍是用我需要的身份声明扩展当前令牌,例如 ClaimTypes.Role
。
我的代码如下:
// Add authentication (Azure AD)
services
.AddAuthentication(sharedOptions =>
sharedOptions.DefaultScheme = JwtBearerDefaults.AuthenticationScheme;
sharedOptions.DefaultAuthenticateScheme = JwtBearerDefaults.AuthenticationScheme;
sharedOptions.DefaultChallengeScheme = JwtBearerDefaults.AuthenticationScheme; // Use JWT as ChallendgeSchema, if not, ASPNet Identity will be used by default and this will return a default non-existing endpoint (because it is not created): Account/Login; https://***.com/questions/45878166/asp-net-core-2-0-disable-automatic-challenge
)
.AddJwtBearer(options =>
options.Audience = this.Configuration["AzureAd:ClientId"];
options.Authority = $"this.Configuration["AzureAd:Instance"]this.Configuration["AzureAd:TenantId"]";
// Added events which checks if the user (token user) exists in our own database, if not then the user is being added with a 'User' role
options.Events = new JwtBearerEvents()
OnTokenValidated = context =>
// Check if roles are present
CheckRoles cr = new CheckRoles();
cr.CreateRoles(services.BuildServiceProvider());
// Check if the user has an OID claim(oid = object id = user id)
if (!context.Principal.HasClaim(c => c.Type == "http://schemas.microsoft.com/identity/claims/objectidentifier"))
context.Fail($"The claim 'oid' is not present in the token.");
ClaimsPrincipal userPrincipal = context.Principal;
CheckUser cu = new CheckUser();
cu.CreateUser(userPrincipal, services.BuildServiceProvider());
// Extend the current token with my (test) Role claim
var claims = new List<Claim>
new Claim(ClaimTypes.Role, "Admin")
;
var appIdentity = new ClaimsIdentity(claims);
context.Principal.AddIdentity(appIdentity);
return Task.CompletedTask;
;
);
遗憾的是,上述方法不起作用,从前端调用 API 时,令牌保持不变,并且没有添加 RoleClaim。任何人都知道如何将我的 RoleClaim 添加到令牌中,以便我可以使用我的策略?
【问题讨论】:
【参考方案1】:在我的情况下,调用 API 时,用户具有特定的角色(管理员)。 此角色有一些角色声明。
如果用户在主体对象中有role.read
作为ClaimTypes.Role
,您可以在Startup.cs
中创建如下策略 -
public void ConfigureServices(IServiceCollection services)
...
services.AddAuthorization(options =>
options.AddPolicy("View Roles", policyBuilder =>
policyBuilder.RequireAuthenticatedUser()
.RequireAssertion(context =>
context.User.HasClaim(ClaimTypes.Role, "role.read"))
.Build();
);
);
...
更新
您需要在声明身份中添加JwtBearerDefaults.AuthenticationScheme
身份验证类型,以便与默认方案匹配。
services
.AddAuthentication(sharedOptions =>
...
)
.AddJwtBearer(options =>
...
options.Events = new JwtBearerEvents()
OnTokenValidated = context =>
...
var appIdentity = new ClaimsIdentity(claims,
JwtBearerDefaults.AuthenticationScheme);
^^^^^
context.Principal.AddIdentity(appIdentity);
return Task.CompletedTask;
;
);
【讨论】:
感谢您的回复!问题是用户的令牌不包含角色声明,因为它是从 Azure 令牌中获取的,我需要找到一种方法来使用我的身份角色声明扩展当前令牌。有关更多信息,请参阅我的主要帖子中的更新部分。 您可以添加身份验证类型var appIdentity = new ClaimsIdentity(claims, JwtBearerDefaults.AuthenticationScheme);
。在使用Authorize
属性进行测试之前,创建一个 MVC 页面,并在浏览器中列出主体对象内的所有声明以进行调试。
看起来它正在工作!我想将JwtBearerDefaults.AuthenticationScheme
添加到new ClaimsIdentity
就可以了!明天将使用真实的用户角色/用户声明而不是我的硬编码管理员角色对其进行更多测试。谢谢!!以上是关于授权策略总是返回 403(禁止)-MVC/API的主要内容,如果未能解决你的问题,请参考以下文章
Spring Security 总是返回 403 被禁止,访问被拒绝
Laravel 8 Policy 总是返回“此操作未经授权”。
Authorize Policy 属性总是返回 403 禁止使用 .net core Identity 和 JwtBearerAuthentication