AspNet CoreIdentityと統合されたIdentityServer 4(IS4)に正常にログインした後、役割の要求を含む問題が発生しています。これにより、「Authorize(Roles = xxx)」属性を使用してAPIへのアクセスを保護できなくなります。
Identity Server 4/AspNetIdentity統合ドキュメントで提供されているサンプルに従っていました。驚いたことに、ドキュメントには、非常に一般的なシナリオであると私が思う役割の主張を含める例がありません。
ホストで指定されたHybridClientCredential付与タイプを使用してIS4ドキュメントに従って3つのプロジェクトをセットアップし、AspNet Identity DBを作成し、EF Coreによって生成されたデータベースにロール(「管理者」)を手動で追加します。正しく設定すれば、ログインに成功した後、ロールがユーザークレームに自動的に含まれることを期待しています。
これは私が使用しているコードです:
ホスト:
public class Config
{
public static IEnumerable<ApiResource> GetApiResources()
{
return new[]
{
// expanded version if more control is needed
new ApiResource
{
Name = "api1",
Description = "My API",
// secret for using introspection endpoint
ApiSecrets =
{
new Secret("secret".Sha256())
},
// include the following using claims in access token (in addition to subject id)
UserClaims = { "role" },
// this API defines two scopes
Scopes =
{
new Scope()
{
Name = "api1",
DisplayName = "Full access to API 1",
UserClaims = new [] { "role" }
}
}
}
};
}
public static IEnumerable<IdentityResource> GetIdentityResources()
{
return new List<IdentityResource>
{
new IdentityResources.OpenId(),
new IdentityResources.Profile()
};
}
public static IEnumerable<Client> GetClients()
{
return new List<Client>()
{
new Client
{
ClientId = "mvc",
ClientName = "MVC Client",
AllowedGrantTypes = GrantTypes.HybridAndClientCredentials,
RequireConsent = false,
ClientSecrets =
{
new Secret("secret".Sha256())
},
RedirectUris = { "http://localhost:5002/signin-oidc" },
PostLogoutRedirectUris = { "http://localhost:5002/signout-callback-oidc" },
AlwaysIncludeUserClaimsInIdToken = true,
AllowedScopes =
{
IdentityServerConstants.StandardScopes.OpenId,
IdentityServerConstants.StandardScopes.Profile,
"api1"
},
AllowOfflineAccess = true
}
};
}
}
クライアント:
public class Startup
{
public Startup(IHostingEnvironment env)
{
var builder = new ConfigurationBuilder()
.SetBasePath(env.ContentRootPath)
.AddJsonFile("appsettings.json", optional: false, reloadOnChange: true)
.AddJsonFile($"appsettings.{env.EnvironmentName}.json", optional: true)
.AddEnvironmentVariables();
Configuration = builder.Build();
}
public IConfigurationRoot Configuration { get; }
// This method gets called by the runtime. Use this method to add services to the container.
public void ConfigureServices(IServiceCollection services)
{
// Add framework services.
services.AddMvc();
}
// This method gets called by the runtime. Use this method to configure the HTTP request pipeline.
public void Configure(IApplicationBuilder app, IHostingEnvironment env, ILoggerFactory loggerFactory)
{
loggerFactory.AddConsole(Configuration.GetSection("Logging"));
loggerFactory.AddDebug();
if (env.IsDevelopment())
{
app.UseDeveloperExceptionPage();
app.UseBrowserLink();
}
else
{
app.UseExceptionHandler("/Home/Error");
}
app.UseStaticFiles();
app.UseCookieAuthentication(new CookieAuthenticationOptions
{
AuthenticationScheme = "Cookies"
});
JwtSecurityTokenHandler.DefaultInboundClaimTypeMap.Clear();
app.UseOpenIdConnectAuthentication(new OpenIdConnectOptions
{
AuthenticationScheme = "oidc",
SignInScheme = "Cookies",
Authority = "http://localhost:5000",
RequireHttpsMetadata = false,
ClientId = "mvc",
ClientSecret = "secret",
ResponseType = "code id_token",
Scope = { "api1", "offline_access" },
GetClaimsFromUserInfoEndpoint = true,
SaveTokens = true,
TokenValidationParameters = new TokenValidationParameters
{
NameClaimType = JwtClaimTypes.Name,
RoleClaimType = JwtClaimTypes.Role,
}
});
app.UseMvc(routes =>
{
routes.MapRoute(
name: "default",
template: "{controller=Home}/{action=Index}/{id?}");
});
}
}
Api:
[Route("api/[controller]")]
[Authorize(Roles="Admin")]
public class ValuesController : Controller
{
// GET api/values
[HttpGet]
public IEnumerable<string> Get()
{
return new string[] { "value1", "value2" };
}
// GET api/values/5
[HttpGet("{id}")]
public string Get(int id)
{
return "value";
}
// POST api/values
[HttpPost]
public void Post([FromBody]string value)
{
}
// PUT api/values/5
[HttpPut("{id}")]
public void Put(int id, [FromBody]string value)
{
}
// DELETE api/values/5
[HttpDelete("{id}")]
public void Delete(int id)
{
}
}
ログインに成功した後、ホストに役割の要求が含まれていないことを除いて、セットアップは機能しました。
この問題を解決する方法を誰かに教えてもらえないかと思っていました。ありがとう。
こんにちはこれは私がカスタムポリシーの役割ベースを作成する方法です、
1)
[構成]-> [クライアント]セクションで、以下を追加します。
Claims = new Claim[]
{
new Claim("Role", "admin")
}
2)
次に、Api-> startup.cs-> ConfigureServicesで以下を追加します。
services.AddAuthorization(options =>
{
options.AddPolicy("admin", policyAdmin =>
{
policyAdmin.RequireClaim("client_Role", "Admin");
});
//otherwise you already have "api1" as scope
options.AddPolicy("admin", builder =>
{
builder.RequireScope("api1");
});
});
3)
次に、次のように使用します。
[Route("api/[controller]")]
[Authorize("admin")]
public class ValuesController : Controller
トークンを分析すると、次のようになります。
{
"alg": "RS256",
"kid": "2f2fcd9bc8c2e54a1f29acf77b2f1d32",
"typ": "JWT"
}
{
"nbf": 1513935820,
"exp": 1513937620,
"iss": "http://localhost/identityserver",
"aud": [
"http://localhost/identityserver/resources",
"MySecuredApi"
],
"client_id": "adminClient",
"client_Role": "admin", <---------------
"scope": [
"api.full_access",
"api.read_only"
]
}
PS:
identity Server 4を使用する場合、次の理由で「RequireRole」を使用できません。
Claims = new Claim[]
{
new Claim("Role", "admin")
},
作成します:
client_Role: admin
ただし、「RequireRole」は以下を使用します。
Role: admin
一致しません。
あなたがテストできるように:
using System.Security.Claims;
MessageBox.Show("" + new Claim("Role", "admin"));
RequireRoleで更新: "ClientClaimsPrefix"をクリア
[構成]-> [クライアント]セクションで、以下を追加します。
ClientClaimsPrefix = "",
Claims = new Claim[]
{
new Claim(ClaimTypes.Role, "Admin")
}
次に、Api-> startup.cs-> ConfigureServicesで以下を追加します。
services.AddAuthorization(options =>
{
options.AddPolicy("admin", builder =>
{
builder.RequireRole(new[] { "Admin" });
});
});
次に、次のように使用します。
[Route("api/[controller]")]
[Authorize("admin")]
public class ValuesController : Controller
それ以外の場合、「ポリシー」なしで次のように使用します。
[Authorize(Roles = "Admin")]