使用 IdentityServer4 对 Web API 进行基于角色的授权
Posted
技术标签:
【中文标题】使用 IdentityServer4 对 Web API 进行基于角色的授权【英文标题】:Role Based Authorization for Web API with IdentityServer4 【发布时间】:2018-05-10 05:32:14 【问题描述】:我将 IdentityServer4 (v2.2.1) 与 .Net Core 2.0 和 Asp.Net Core Identity 一起使用。 我的解决方案中有三个项目。
-
身份服务器
网络 API
MVC Web 应用程序
我正在尝试在我的 Web API 上实现基于角色的授权,以便任何客户端将访问令牌传递给 Web API 以访问资源。
目前我可以在 MVC 应用程序控制器上实现基于角色的授权,但我无法为 WEB API 控制器传递/配置相同的权限。
以下是身份服务器文件: 配置文件
public static IEnumerable<ApiResource> GetApiResources()
return new List<ApiResource>
//SCOPE - Resource to be protected by IDS
new ApiResource("TCSAPI", "TCS API")
UserClaims = "role"
;
public static IEnumerable<Client> GetClients()
return new List<Client>
new Client
ClientId = "TCSIdentity",
ClientName = "TCS Mvc Client Application .",
AllowedGrantTypes = GrantTypes.HybridAndClientCredentials,
RequireConsent = false,
ClientSecrets =
new Secret("secret".Sha256())
,
RedirectUris = "http://localhost:5002/signin-oidc" ,
PostLogoutRedirectUris = "http://localhost:5002/signout-callback-oidc" ,
AlwaysSendClientClaims= true,
AlwaysIncludeUserClaimsInIdToken = true,
AllowedScopes =
IdentityServerConstants.StandardScopes.OpenId,
IdentityServerConstants.StandardScopes.Email,
IdentityServerConstants.StandardScopes.Profile,
IdentityServerConstants.StandardScopes.OfflineAccess,
"TCSAPI",
"office",
"role",
,
AllowOfflineAccess = true
;
public static IEnumerable<IdentityResource> GetIdentityResources()
return new IdentityResource[]
new IdentityResources.OpenId(),
new IdentityResources.Profile(),
new IdentityResources.Email(),
new IdentityResource
Name = "role",
DisplayName="User Role",
Description="The application can see your role.",
UserClaims = new[]JwtClaimTypes.Role,ClaimTypes.Role,
ShowInDiscoveryDocument = true,
Required=true,
Emphasize = true
;
Startup.cs
public void ConfigureServices(IServiceCollection services)
services.AddDbContext<ApplicationDbContext>(options =>
options.UseSqlServer(Configuration.GetConnectionString("DefaultConnection")));
services.AddIdentity<ApplicationUser, IdentityRole>()
.AddEntityFrameworkStores<ApplicationDbContext>()
.AddDefaultTokenProviders();
// Add application services.
services.AddTransient<IEmailSender, EmailSender>();
services.AddMvc();
// configure identity server with in-memory stores, keys, clients and scopes
services.AddIdentityServer()
.AddDeveloperSigningCredential()
.AddInMemoryPersistedGrants()
.AddInMemoryIdentityResources(Config.GetIdentityResources())
.AddInMemoryApiResources(Config.GetApiResources())
.AddInMemoryClients(Config.GetClients())
.AddAspNetIdentity<ApplicationUser>();
MVC WEB APP(Roles Base Authorization 适用于 MVC WEB APP):
RoleClaimAction.cs 使用此文件将角色添加到身份。
internal class RoleClaimAction : ClaimAction
public RoleClaimAction()
: base("role", ClaimValueTypes.String)
public override void Run(JObject userData, ClaimsIdentity identity, string issuer)
var tokens = userData.SelectTokens("role");
IEnumerable<string> roles;
foreach (var token in tokens)
if (token is JArray)
var jarray = token as JArray;
roles = jarray.Values<string>();
else
roles = new string[] token.Value<string>() ;
foreach (var role in roles)
Claim claim = new Claim("role", role, ValueType, issuer);
if (!identity.HasClaim(c => c.Subject == claim.Subject
&& c.Value == claim.Value))
identity.AddClaim(claim);
MVC WEB APP/Startup.cs
public void ConfigureServices(IServiceCollection services)
services.AddMvc();
JwtSecurityTokenHandler.DefaultInboundClaimTypeMap.Clear();
services.AddCors();
services.AddAuthentication(options =>
options.DefaultScheme = "Cookies";
options.DefaultChallengeScheme = "oidc";
)
.AddCookie("Cookies")
.AddOpenIdConnect("oidc", options =>
options.SignInScheme = "Cookies";
options.Authority = "http://localhost:5000";
options.RequireHttpsMetadata = false;
options.ClientId = "TCSIdentity";
//HYBRID FLOW
options.ClientSecret = "secret";
options.ClaimActions.Add(new RoleClaimAction()); // <--
options.ResponseType = "code id_token token";
options.GetClaimsFromUserInfoEndpoint = true;
options.Scope.Add("TCSAPI");
options.Scope.Add("offline_access");
//END HYBRID FLOW
options.SaveTokens = true;
options.Scope.Add("role");
options.TokenValidationParameters.NameClaimType = "name";
options.TokenValidationParameters.RoleClaimType = "role";
);
MVC WEB APP/HomeController.cs 此操作方法适用于角色基础授权,但是当我尝试将令牌传递给 Web Api 以使用角色基础授权访问任何内容时,它无法授权。例如 var content = await client.GetStringAsync("http://localhost:5001/user");
[Authorize(Roles = "User")]
[Route("user")]
public async Task<IActionResult> UserAccess()
var tokenClient = new TokenClient("http://localhost:5000/connect/token", "RoleApi", "secret");
var tokenResponse = await tokenClient.RequestClientCredentialsAsync("TCSAPI");
var client = new HttpClient();
client.SetBearerToken(tokenResponse.AccessToken);
var content = await client.GetStringAsync("http://localhost:5001/user");
ViewBag.Json = JArray.Parse(content).ToString();
return View("json");
[Authorize(Roles = "Admin")]
[Route("admin")]
public async Task<IActionResult> AdminAccess()
var accessToken = await HttpContext.GetTokenAsync("id_token");
var client = new HttpClient();
client.SetBearerToken(accessToken);
var content = await client.GetStringAsync("http://localhost:5001/admin");
ViewBag.Json = JArray.Parse(content).ToString();
return View("json");
WEBAPI/Startup.cs
public void ConfigureServices(IServiceCollection services)
services.AddMvcCore()
.AddAuthorization()
.AddJsonFormatters();
services.AddAuthentication("Bearer")
.AddIdentityServerAuthentication(options =>
options.Authority = "http://localhost:5000";
options.RequireHttpsMetadata = false;
options.ApiName = "TCSAPI";
);
services.AddCors(options =>
options.AddPolicy("default", policy =>
policy.WithOrigins("http://localhost:5002")
.AllowAnyHeader()
.AllowAnyMethod();
);
);
WEB API/TestController.cs
[Route("admin")]
[Authorize(Roles = "Admin")]
public IActionResult AdminAccess()
return new JsonResult(from c in User.Claims select new c.Type, c.Value );
[Route("user")]
[Authorize(Roles = "User")]
public IActionResult UserAccess()
return new JsonResult(from c in User.Claims select new c.Type, c.Value );
[AllowAnonymous]
[Route("public")]
public IActionResult PublicAccess()
return new JsonResult(from c in User.Claims select new c.Type, c.Value );
【问题讨论】:
你是怎么解决这个问题的?如果您这样做,请提供信息。谢谢 我添加了一个 Menu Tabble 和一个 MenuPermissions 表。在 UserProfileService 中,我从数据库中读取用户权限并将每个权限添加为声明。在我的控制器操作中,我将声明用作许可。这样现在我可以控制控制器和单个操作方法的权限。 @你能提供最终代码吗? 无法确认。我会检查我是否可以提取所需的代码而不是我将分享的代码。 【参考方案1】:您的代码不完全是基于策略的授权。你的看起来像 .NET Framework 基于角色的授权。
对于Policy Based Authorization,你需要做以下事情:
1.在您的 Web API 项目的 Startup.cs
中,您需要添加如下内容:
// more code
.AddMvcCore()
.AddAuthorization(options =>
options.AddPolicy("Policy1",
policy => policy.Requirements.Add(new Policy1Requirement()));
options.AddPolicy("Policy2",
policy => policy.Requirements.Add(new Policy2Requirement()));
.
.
.
.
)
// more code
2. 那么你需要为每个Policy(X)Requirement
设置一个类:
public class Policy1Requirement : AuthorizationHandler<Policy1Requirement>, IAuthorizationRequirement
protected override Task HandleRequirementAsync(AuthorizationHandlerContext context, AdminUserRequirement requirement)
if (!context.User.HasClaim(c => c.Type == "role" && c.Value == "<YOUR_ROLE_FOR_THIS_POLICY>"))
context.Fail();
else
context.Succeed(requirement);
return Task.FromResult(0);
3.最后,在您应用政策的地方,您需要:
[Authorize(Policy = "Policy1")]
public class MyController : Controller
.
.
祝你好运!
PS:
名称Policy(X)
和Policy(X)Requirement
仅用于说明。你可以使用任何你想要的名字,只要你实现正确的接口IAuthorizationRequirement
,并继承类AuthorizationHandler
【讨论】:
以上是关于使用 IdentityServer4 对 Web API 进行基于角色的授权的主要内容,如果未能解决你的问题,请参考以下文章
自己发行 JWT 令牌与使用 IdentityServer4(OIDC) 进行 Web API
IdentityServer4 + SignalR Core +RabbitMQ 构建web即时通讯
IdentityServer4 基于角色的 Web API 授权与 ASP.NET Core 身份
未包含在 JWT 中且未发送到 Web Api 的 IdentityServer4 用户声明
使用 IdentityServer4.AccessTokenValidation 包向 IdentityServer3 授权 .NET 5 Web API 引用令牌时遇到问题