IdentityServer4 基于角色的授权
Role based authorization with IdentityServer4
我正在尝试使用 IdentityServer4 实现 "Role Based Authorization",以根据用户角色授予对我的 API 的访问权限。
例如,我想为用户设置两个角色,即 FreeUser 和 PaidUser,并希望使用 [Authorize(Roles = "FreeUser")) 通过授权属性授予对 API 的访问权限], 请帮助我如何实现这一目标。
我有以下解决方案结构:
- IdentityServer
- 网络Api
- Javascript 客户
我已按如下方式注册了我的 Javascript 客户端:
new Client
{
ClientId = "js",
ClientName = "javascript client",
AllowedGrantTypes = GrantTypes.Implicit,
AllowAccessTokensViaBrowser= true,
RedirectUris = {"http://localhost:5004/callback.html"},
PostLogoutRedirectUris = {"http://localhost:5004/index.html"},
AllowedCorsOrigins = {"http://localhost:5004"},
AllowedScopes =
{
StandardScopes.OpenId.Name,
StandardScopes.Profile.Name,
"api1",
"role",
StandardScopes.AllClaims.Name
}
}
范围
return new List<Scope>
{
StandardScopes.OpenId,
StandardScopes.Profile,
new Scope
{
Name = "api1",
Description = "My API"
},
new Scope
{
Enabled = true,
Name = "role",
DisplayName = "Role(s)",
Description = "roles of user",
Type = ScopeType.Identity,
Claims = new List<ScopeClaim>
{
new ScopeClaim("role",false)
}
},
StandardScopes.AllClaims
};
用户
return new List<InMemoryUser>
{
new InMemoryUser
{
Subject = "1",
Username = "alice",
Password = "password",
Claims = new List<Claim>
{
new Claim("name", "Alice"),
new Claim("website", "https://alice.com"),
new Claim("role","FreeUser")
}
},
new InMemoryUser
{
Subject = "2",
Username = "bob",
Password = "password",
Claims = new List<Claim>
{
new Claim("name", "Bob"),
new Claim("website", "https://bob.com"),
new Claim("role","PaidUser")
}
}
};
WebApi Startup.cs
public void Configure(IApplicationBuilder app, IHostingEnvironment env, ILoggerFactory loggerFactory)
{
loggerFactory.AddConsole(Configuration.GetSection("Logging"));
loggerFactory.AddDebug();
JwtSecurityTokenHandler.DefaultInboundClaimTypeMap.Clear();
app.UseCors("default");
app.UseIdentityServerAuthentication(new IdentityServerAuthenticationOptions
{
Authority = "http://localhost:5000",
ScopeName = "api1",
// AdditionalScopes = new List<string> { "openid","profile", "role" },
RequireHttpsMetadata = false
});
app.UseMvc();
}
Web Api 控制器
namespace Api.Controllers
{
[Route("[controller]")]
public class IdentityController : ControllerBase
{
[HttpGet]
[Authorize(Roles = "PaidUser")]
public IActionResult Get()
{
return new JsonResult(from c in User.Claims select new { c.Type, c.Value });
}
[Authorize(Roles = "FreeUser")]
[HttpGet]
[Route("getfree")]
public IActionResult GetFreeUser()
{
return new JsonResult(from c in User.Claims select new { c.Type, c.Value });
}
}
}
Javascript 客户 app.js
在这里,我试图通过 IdentityServer 登录用户并发出 API 请求。
var mgr = new Oidc.UserManager(config);
mgr.getUser().then(function (user) {
if (user) {
log("User logged in", user.profile);
} else {
log("User is not logged in.");
}
});
function login() {
mgr.signinRedirect();
}
function api() {
mgr.getUser().then(function (user) {
var url = "http://localhost:5001/identity/getfree";
var xhr = new XMLHttpRequest();
xhr.open("GET", url);
xhr.onload = function () {
log(xhr.status, JSON.parse(xhr.responseText));
};
xhr.setRequestHeader("Authorization", "Bearer " + user.access_token);
xhr.send();
});
}
function logout() {
mgr.signoutRedirect();
}
登录流程正常,我可以成功登录,并且可以在访问令牌中接收角色。
当我通过单击按钮(调用 Api)向 API 发出请求时,出现以下错误。
将new Claim("role","FreeUser")
更改为new Claim(ClaimTypes.Role, "FreeUser")
或者创建这样的策略:
services.AddAuthorization(options =>
{
options.AddPolicy("FreeUser", policy => policy.RequireClaim("role", "FreeUser"));
});
并使用它:
[Authorize(Policy = "FreeUser")]
鉴于您没有为 javascript 客户端提供配置对象,我假设您已按如下方式配置范围。
scope:"openid profile api1 role"
我认为您遇到问题的主要原因是角色声明未包含在您的访问令牌中。
如下将角色声明添加到 api1 范围以将其包含在访问令牌中。
new Scope
{
Name = "api1",
DisplayName = "API1 access",
Description = "My API",
Type = ScopeType.Resource,
IncludeAllClaimsForUser = true,
Claims = new List<ScopeClaim>
{
new ScopeClaim(ClaimTypes.Name),
new ScopeClaim(ClaimTypes.Role)
}
}
您可以在此处阅读我的回答以帮助调试问题。
完整的工作解决方案在这里。
https://github.com/weliwita/IdentityServer4.Samples/tree/40844310
我就此写了一个示例 post
我已经使用 Roles 进行了测试,并声称我也可以在客户端 Web 应用程序和 API 应用程序中使用 [Authorize(Role="SuperAdmin, Admin")]。
我在 .NET 5 中得到了像这样工作的角色:
在Startup.cs
中的services.AddAuthentication
之前添加JwtSecurityTokenHandler.DefaultInboundClaimTypeMap.Clear();
。
我也加了
services.AddScoped<IProfileService, ProfileService>();
和 ProfileService.cs
看起来像这样将角色映射到声明:
public sealed class ProfileService : IProfileService
{
private readonly IUserClaimsPrincipalFactory<ApplicationUser> _userClaimsPrincipalFactory;
private readonly UserManager<ApplicationUser> _userMgr;
private readonly RoleManager<IdentityRole> _roleMgr;
public ProfileService(
UserManager<ApplicationUser> userMgr,
RoleManager<IdentityRole> roleMgr,
IUserClaimsPrincipalFactory<ApplicationUser> userClaimsPrincipalFactory)
{
_userMgr = userMgr;
_roleMgr = roleMgr;
_userClaimsPrincipalFactory = userClaimsPrincipalFactory;
}
public async Task GetProfileDataAsync(ProfileDataRequestContext context)
{
string sub = context.Subject.GetSubjectId();
ApplicationUser user = await _userMgr.FindByIdAsync(sub);
ClaimsPrincipal userClaims = await _userClaimsPrincipalFactory.CreateAsync(user);
List<Claim> claims = userClaims.Claims.ToList();
claims = claims.Where(claim => context.RequestedClaimTypes.Contains(claim.Type)).ToList();
if (_userMgr.SupportsUserRole)
{
IList<string> roles = await _userMgr.GetRolesAsync(user);
foreach (var roleName in roles)
{
claims.Add(new Claim(JwtClaimTypes.Role, roleName));
if (_roleMgr.SupportsRoleClaims)
{
IdentityRole role = await _roleMgr.FindByNameAsync(roleName);
if (role != null)
{
claims.AddRange(await _roleMgr.GetClaimsAsync(role));
}
}
}
}
context.IssuedClaims = claims;
}
public async Task IsActiveAsync(IsActiveContext context)
{
string sub = context.Subject.GetSubjectId();
ApplicationUser user = await _userMgr.FindByIdAsync(sub);
context.IsActive = user != null;
}
}
来源:
https://ffimnsr.medium.com/adding-identity-roles-to-identity-server-4-in-net-core-3-1-d42b64ff6675
我正在尝试使用 IdentityServer4 实现 "Role Based Authorization",以根据用户角色授予对我的 API 的访问权限。
例如,我想为用户设置两个角色,即 FreeUser 和 PaidUser,并希望使用 [Authorize(Roles = "FreeUser")) 通过授权属性授予对 API 的访问权限], 请帮助我如何实现这一目标。
我有以下解决方案结构:
- IdentityServer
- 网络Api
- Javascript 客户
我已按如下方式注册了我的 Javascript 客户端:
new Client
{
ClientId = "js",
ClientName = "javascript client",
AllowedGrantTypes = GrantTypes.Implicit,
AllowAccessTokensViaBrowser= true,
RedirectUris = {"http://localhost:5004/callback.html"},
PostLogoutRedirectUris = {"http://localhost:5004/index.html"},
AllowedCorsOrigins = {"http://localhost:5004"},
AllowedScopes =
{
StandardScopes.OpenId.Name,
StandardScopes.Profile.Name,
"api1",
"role",
StandardScopes.AllClaims.Name
}
}
范围
return new List<Scope>
{
StandardScopes.OpenId,
StandardScopes.Profile,
new Scope
{
Name = "api1",
Description = "My API"
},
new Scope
{
Enabled = true,
Name = "role",
DisplayName = "Role(s)",
Description = "roles of user",
Type = ScopeType.Identity,
Claims = new List<ScopeClaim>
{
new ScopeClaim("role",false)
}
},
StandardScopes.AllClaims
};
用户
return new List<InMemoryUser>
{
new InMemoryUser
{
Subject = "1",
Username = "alice",
Password = "password",
Claims = new List<Claim>
{
new Claim("name", "Alice"),
new Claim("website", "https://alice.com"),
new Claim("role","FreeUser")
}
},
new InMemoryUser
{
Subject = "2",
Username = "bob",
Password = "password",
Claims = new List<Claim>
{
new Claim("name", "Bob"),
new Claim("website", "https://bob.com"),
new Claim("role","PaidUser")
}
}
};
WebApi Startup.cs
public void Configure(IApplicationBuilder app, IHostingEnvironment env, ILoggerFactory loggerFactory)
{
loggerFactory.AddConsole(Configuration.GetSection("Logging"));
loggerFactory.AddDebug();
JwtSecurityTokenHandler.DefaultInboundClaimTypeMap.Clear();
app.UseCors("default");
app.UseIdentityServerAuthentication(new IdentityServerAuthenticationOptions
{
Authority = "http://localhost:5000",
ScopeName = "api1",
// AdditionalScopes = new List<string> { "openid","profile", "role" },
RequireHttpsMetadata = false
});
app.UseMvc();
}
Web Api 控制器
namespace Api.Controllers
{
[Route("[controller]")]
public class IdentityController : ControllerBase
{
[HttpGet]
[Authorize(Roles = "PaidUser")]
public IActionResult Get()
{
return new JsonResult(from c in User.Claims select new { c.Type, c.Value });
}
[Authorize(Roles = "FreeUser")]
[HttpGet]
[Route("getfree")]
public IActionResult GetFreeUser()
{
return new JsonResult(from c in User.Claims select new { c.Type, c.Value });
}
}
}
Javascript 客户 app.js 在这里,我试图通过 IdentityServer 登录用户并发出 API 请求。
var mgr = new Oidc.UserManager(config);
mgr.getUser().then(function (user) {
if (user) {
log("User logged in", user.profile);
} else {
log("User is not logged in.");
}
});
function login() {
mgr.signinRedirect();
}
function api() {
mgr.getUser().then(function (user) {
var url = "http://localhost:5001/identity/getfree";
var xhr = new XMLHttpRequest();
xhr.open("GET", url);
xhr.onload = function () {
log(xhr.status, JSON.parse(xhr.responseText));
};
xhr.setRequestHeader("Authorization", "Bearer " + user.access_token);
xhr.send();
});
}
function logout() {
mgr.signoutRedirect();
}
登录流程正常,我可以成功登录,并且可以在访问令牌中接收角色。
当我通过单击按钮(调用 Api)向 API 发出请求时,出现以下错误。
将new Claim("role","FreeUser")
更改为new Claim(ClaimTypes.Role, "FreeUser")
或者创建这样的策略:
services.AddAuthorization(options =>
{
options.AddPolicy("FreeUser", policy => policy.RequireClaim("role", "FreeUser"));
});
并使用它:
[Authorize(Policy = "FreeUser")]
鉴于您没有为 javascript 客户端提供配置对象,我假设您已按如下方式配置范围。
scope:"openid profile api1 role"
我认为您遇到问题的主要原因是角色声明未包含在您的访问令牌中。
如下将角色声明添加到 api1 范围以将其包含在访问令牌中。
new Scope
{
Name = "api1",
DisplayName = "API1 access",
Description = "My API",
Type = ScopeType.Resource,
IncludeAllClaimsForUser = true,
Claims = new List<ScopeClaim>
{
new ScopeClaim(ClaimTypes.Name),
new ScopeClaim(ClaimTypes.Role)
}
}
您可以在此处阅读我的回答以帮助调试问题。
完整的工作解决方案在这里。 https://github.com/weliwita/IdentityServer4.Samples/tree/40844310
我就此写了一个示例 post
我已经使用 Roles 进行了测试,并声称我也可以在客户端 Web 应用程序和 API 应用程序中使用 [Authorize(Role="SuperAdmin, Admin")]。
我在 .NET 5 中得到了像这样工作的角色:
在Startup.cs
中的services.AddAuthentication
之前添加JwtSecurityTokenHandler.DefaultInboundClaimTypeMap.Clear();
。
我也加了
services.AddScoped<IProfileService, ProfileService>();
和 ProfileService.cs
看起来像这样将角色映射到声明:
public sealed class ProfileService : IProfileService
{
private readonly IUserClaimsPrincipalFactory<ApplicationUser> _userClaimsPrincipalFactory;
private readonly UserManager<ApplicationUser> _userMgr;
private readonly RoleManager<IdentityRole> _roleMgr;
public ProfileService(
UserManager<ApplicationUser> userMgr,
RoleManager<IdentityRole> roleMgr,
IUserClaimsPrincipalFactory<ApplicationUser> userClaimsPrincipalFactory)
{
_userMgr = userMgr;
_roleMgr = roleMgr;
_userClaimsPrincipalFactory = userClaimsPrincipalFactory;
}
public async Task GetProfileDataAsync(ProfileDataRequestContext context)
{
string sub = context.Subject.GetSubjectId();
ApplicationUser user = await _userMgr.FindByIdAsync(sub);
ClaimsPrincipal userClaims = await _userClaimsPrincipalFactory.CreateAsync(user);
List<Claim> claims = userClaims.Claims.ToList();
claims = claims.Where(claim => context.RequestedClaimTypes.Contains(claim.Type)).ToList();
if (_userMgr.SupportsUserRole)
{
IList<string> roles = await _userMgr.GetRolesAsync(user);
foreach (var roleName in roles)
{
claims.Add(new Claim(JwtClaimTypes.Role, roleName));
if (_roleMgr.SupportsRoleClaims)
{
IdentityRole role = await _roleMgr.FindByNameAsync(roleName);
if (role != null)
{
claims.AddRange(await _roleMgr.GetClaimsAsync(role));
}
}
}
}
context.IssuedClaims = claims;
}
public async Task IsActiveAsync(IsActiveContext context)
{
string sub = context.Subject.GetSubjectId();
ApplicationUser user = await _userMgr.FindByIdAsync(sub);
context.IsActive = user != null;
}
}
来源:
https://ffimnsr.medium.com/adding-identity-roles-to-identity-server-4-in-net-core-3-1-d42b64ff6675