问题描述
我有一个由IdentityServer4保护的asp.net核心mvc Web应用程序和一个asp.net核心api项目。 当用户登录(到mvc应用程序)时,我添加了一个声明(“ mycompany”),我希望可以在api项目中对其进行访问。
我可以访问mvc项目中的附加声明,但是不能访问api项目中的新声明。
如何使声明(“ mycompany”)显示在api项目中?
MVC App Startup.cs
services.AddAuthentication(options =>
{
options.DefaultScheme = "Cookies";
options.DefaultChallengeScheme = "oidc";
})
.AddCookie("Cookies").AddOpenIdConnect("oidc",options =>
{
options.Authority = Configuration.GetSection("IdpConfig").GetValue<string>("IdpUri");
options.RequireHttpsMetadata = false;
options.ClientId = "MyProject.Web";
options.ClientSecret = "SomeSecret";
options.ResponseType = "code id_token";
options.Savetokens = true;
options.Events = new OpenIdConnectEvents
{
OnTicketReceived = async (context) =>
{
try
{
var claimsIdentity = context.Principal.Identity as ClaimsIdentity;
claimsIdentity.AddClaim(new Claim("mycompany","some company"));
await Task.Fromresult(0);
}
catch (Exception ex)
{
// Log This
}
},OnUserinformationReceived = (context) => Task.Fromresult(0),OnTokenValidated = (context) =>
{
return Task.Fromresult(0);
},OnRedirectToIdentityProvider = (context) => Task.CompletedTask
};
});
在MVC应用程序中,此方法有效(返回“某家公司”),但是在api中,该声明不存在
public static string GetCompany(this ClaimsPrincipal principal)
{
var company = principal.Claims.FirstOrDefault(c => c.Type == "mycompany");
return company?.Value;
}
解决方法
您在MVC应用程序中添加的声明已添加到本地身份,您需要将新声明添加到发送到API的访问令牌中。您可以将该声明添加到IdentitySever中的APIScope / APIResources中。您无法在MVC应用程序中更改访问权限,因为令牌的签名将不再正确。
,我认为它起作用的方式
在身份服务器上,您可以为用户添加新的Claim(AspNetUserClaims表),并为其指定公司名称和值TEST COMPANY。 在IdentityClaims表内添加声明公司,并将该记录引用到配置文件范围(IdentityResources表)的IdentityResourceId中,以便当您的客户端应用要求配置文件范围IS时,将使用公司声明及其值填充AccessToken。
配置MVC客户端以映射自定义声明并能够询问所需的作用域
options.ClaimActions.MapUniqueJsonKey("MyCompany","company");
//two trips to load claims into the cookie
//but the id token is smaller
options.GetClaimsFromUserInfoEndpoint = true;
//configure scope
options.Scope.Clear();
options.Scope.Add("openid");
options.Scope.Add("profile");
如果您获得访问令牌并在JWT.IO上进行了检查,则应包含具有其价值的自定义声明公司。
var accessToken = await HttpContext.GetTokenAsync("access_token");
在您的MVC客户中,您可以获得这样的公司名称:
var companyName = User.Claims.Where(x => x.Type == "MyCompany").FirstOrDefault()?.Value;
,
要访问API项目中的自定义声明,您需要在JWT访问令牌中包括这些声明。对于IdentityServer4,您需要实现IProfileService
接口。
例如
public class UserProfileService : IProfileService
{
public Task GetProfileDataAsync(ProfileDataRequestContext context)
{
// Ensure adding claims to access token only
if (context.Caller.Equals("ClaimsProviderAccessToken",StringComparison.OrdinalIgnoreCase))
{
var claims = new List<Claim>();
claims.Add(new Claim("<CUSTOM_CLAIM_TYPE>","value",ClaimValueTypes.String));
context.RequestedClaimTypes = new[] { "<CUSTOM_CLAIM_TYPE>" };
context.AddRequestedClaims(claims);
}
return Task.CompletedTask;
}
public Task IsActiveAsync(IsActiveContext context)
{
context.IsActive = true;
return Task.CompletedTask;
}
}
您还可以使用IUserClaimsPrincipalFactory
向身份添加自定义声明,然后通过GetProfileDataAsync()
在RequestedClaimTypes
方法中对其进行过滤。
然后在ConfigureServices()
类的Startup
方法中,向UserProfileService
注册IIdentityServerBuilder
:
services.AddIdentityServer(...)
.AddProfileService<UserProfileService>();
现在,您的访问令牌具有自定义声明,您可以在API项目中访问该声明,例如,通过控制器的User
属性。
正如评论中已经提到的那样,建议尽可能减少访问令牌,仅在需要时根据访问令牌中的用户ID获取自定义声明。 >