使用 IdentityServer 4 时如何在 Api 项目中添加额外声明
How to add additional claims in Api Project when using IdentityServer 4
对不起我的英语。
我有三个项目:IdentityServer、Ensino.Mvc、Ensino.Api。 IdentityServer 项目提供了主要的身份信息和声明 - claim Profile、claim Address、claim Sid...等,来自 IdentityServer4 库。 Ensino.Mvc 项目在令牌中获取此信息并将其发送到 API,以便授予 MVC 对资源的访问权限。令牌包含 IdentityServer 提供的所有声明。但是在 API 中,我需要生成其他 API 特定的声明,例如:声明 EnrollmentId 对应于令牌中的声明 Sid。而且我还想在 HttpContext 中添加此声明以供将来使用。有人可以告诉我如何实现吗?
我在 Startup.ConfigureServices 中有此代码:
// Add identity services
services.AddAuthentication("Bearer")
.AddIdentityServerAuthentication(options =>
{
options.Authority = "http://localhost:5100";
options.RequireHttpsMetadata = false;
options.ApiName = "beehouse.scope.ensino-api";
});
// Add mvc services
services.AddMvc();
在其他项目中,没有 API,只有 mvc,我继承了 UserClaimsPrincipalFactory
并覆盖了 CreateAsync
以添加额外的声明。我喜欢做这样的事情,但在 API 项目中。可能吗?
执行此操作的最佳方法是什么?
编辑:经过一些研究,我想做的是:根据声明和特定的 api 数据库数据,通过 IdentityServer 进行身份验证并在 api 中设置授权。
好的,一步一步来:
- 您需要在 Identity Server 中创建一个 API 资源(在您的情况下为
beehouse.scope.ensino-api
,但我建议您在此处发布代码时隐藏此类信息)。它应该与您的 options.ApiName
同名
- 您需要将此范围添加到您的 MVC 客户端的允许范围。
对这两个步骤进行了描述 here,但主要是在添加资源时,您可以执行以下操作:
new ApiResource("beehouse.scope.ensino-api", "My test resource", new List<string>() { "claim1", "claim2" });
然后在您的客户端配置中:
new Client
{
ClientId = "client",
.
.
// scopes that client has access to
AllowedScopes = { "beehouse.scope.ensino-api" }
.
.
}
这会将与此资源关联的声明添加到令牌中。
当然,您必须在 Identity Server 级别设置此声明,但根据您所说的,您已经知道如何执行此操作。
在您的 API 项目中,您可以将自己的事件处理程序添加到 options.JwtBearerEvents.OnTokenValidated
。这是 ClaimsPrincipal
已设置的位置,您可以向身份添加声明或向主体添加新身份。
services.AddAuthentication("Bearer")
.AddIdentityServerAuthentication(options =>
{
options.Authority = "http://localhost:5100";
options.RequireHttpsMetadata = false;
options.ApiName = "beehouse.scope.ensino-api";
options.JwtBearerEvents.OnTokenValidated = async (context) =>
{
var identity = context.Principal.Identity as ClaimsIdentity;
// load user specific data from database
...
// add claims to the identity
identity.AddClaim(new Claim("Type", "Value"));
};
});
请注意,这将 运行 对 API 的每个请求进行处理,因此如果您从数据库加载信息,最好缓存声明。
另外,Identity Server 应该只负责识别用户,而不是他们做什么。他们所做的是特定于应用程序的(角色、权限等),因此您认识到这一点并避免与 Identity Server 的逻辑交叉是正确的。
使用 IdentityServerAuthenticationHandler
制作自己的 AuthenticationHandler
将是最佳选择。这将允许您使用 DI、拒绝身份验证并在不需要时跳过自定义身份验证处理程序。
首先验证令牌然后添加更多声明的示例AuthenticationHandler
:
public class MyApiAuthenticationHandler : AuthenticationHandler<AuthenticationSchemeOptions>
{
protected override async Task<AuthenticateResult> HandleAuthenticateAsync()
{
// Pass authentication to IdentityServerAuthenticationHandler
var authenticateResult = await Context.AuthenticateAsync("Bearer");
// If token authentication fails, return immediately
if (!authenticateResult.Succeeded)
{
return authenticateResult;
}
// Get user ID from token
var userId = authenticateResult.Principal.Claims
.FirstOrDefault(c => c.Type == JwtClaimTypes.Subject)?.Value;
// Do additional checks for authentication
// e.g. lookup user ID in database
if (userId == null)
{
return AuthenticateResult.NoResult();
}
// Add additional claims
var identity = authenticateResult.Principal.Identity as ClaimsIdentity;
identity.AddClaim(new Claim("MyClaim", "MyValue"));
return authenticateResult;
}
}
向服务添加处理程序:
services.AddAuthentication()
.AddIdentityServerAuthentication(options =>
{
// ...
})
.AddScheme<AuthenticationSchemeOptions, MyApiAuthenticationHandler>("MyApiScheme", null);
现在您可以使用任一方案:
// Authenticate token and get extra API claims
[Authorize(AuthenticationSchemes = "MyApiScheme")]
// Authenticate just the token
[Authorize(AuthenticationSchemes = "Bearer")]
注意 IdentityServerAuthenticationHandler
做同样的事情,using the dotnet JWT handler:
public class IdentityServerAuthenticationHandler : AuthenticationHandler<IdentityServerAuthenticationOptions>
{
protected override async Task<AuthenticateResult> HandleAuthenticateAsync()
{
...
return await Context.AuthenticateAsync(jwtScheme);
...
}
}
对不起我的英语。
我有三个项目:IdentityServer、Ensino.Mvc、Ensino.Api。 IdentityServer 项目提供了主要的身份信息和声明 - claim Profile、claim Address、claim Sid...等,来自 IdentityServer4 库。 Ensino.Mvc 项目在令牌中获取此信息并将其发送到 API,以便授予 MVC 对资源的访问权限。令牌包含 IdentityServer 提供的所有声明。但是在 API 中,我需要生成其他 API 特定的声明,例如:声明 EnrollmentId 对应于令牌中的声明 Sid。而且我还想在 HttpContext 中添加此声明以供将来使用。有人可以告诉我如何实现吗?
我在 Startup.ConfigureServices 中有此代码:
// Add identity services
services.AddAuthentication("Bearer")
.AddIdentityServerAuthentication(options =>
{
options.Authority = "http://localhost:5100";
options.RequireHttpsMetadata = false;
options.ApiName = "beehouse.scope.ensino-api";
});
// Add mvc services
services.AddMvc();
在其他项目中,没有 API,只有 mvc,我继承了 UserClaimsPrincipalFactory
并覆盖了 CreateAsync
以添加额外的声明。我喜欢做这样的事情,但在 API 项目中。可能吗?
执行此操作的最佳方法是什么?
编辑:经过一些研究,我想做的是:根据声明和特定的 api 数据库数据,通过 IdentityServer 进行身份验证并在 api 中设置授权。
好的,一步一步来:
- 您需要在 Identity Server 中创建一个 API 资源(在您的情况下为
beehouse.scope.ensino-api
,但我建议您在此处发布代码时隐藏此类信息)。它应该与您的options.ApiName
同名
- 您需要将此范围添加到您的 MVC 客户端的允许范围。
对这两个步骤进行了描述 here,但主要是在添加资源时,您可以执行以下操作:
new ApiResource("beehouse.scope.ensino-api", "My test resource", new List<string>() { "claim1", "claim2" });
然后在您的客户端配置中:
new Client
{
ClientId = "client",
.
.
// scopes that client has access to
AllowedScopes = { "beehouse.scope.ensino-api" }
.
.
}
这会将与此资源关联的声明添加到令牌中。 当然,您必须在 Identity Server 级别设置此声明,但根据您所说的,您已经知道如何执行此操作。
在您的 API 项目中,您可以将自己的事件处理程序添加到 options.JwtBearerEvents.OnTokenValidated
。这是 ClaimsPrincipal
已设置的位置,您可以向身份添加声明或向主体添加新身份。
services.AddAuthentication("Bearer")
.AddIdentityServerAuthentication(options =>
{
options.Authority = "http://localhost:5100";
options.RequireHttpsMetadata = false;
options.ApiName = "beehouse.scope.ensino-api";
options.JwtBearerEvents.OnTokenValidated = async (context) =>
{
var identity = context.Principal.Identity as ClaimsIdentity;
// load user specific data from database
...
// add claims to the identity
identity.AddClaim(new Claim("Type", "Value"));
};
});
请注意,这将 运行 对 API 的每个请求进行处理,因此如果您从数据库加载信息,最好缓存声明。
另外,Identity Server 应该只负责识别用户,而不是他们做什么。他们所做的是特定于应用程序的(角色、权限等),因此您认识到这一点并避免与 Identity Server 的逻辑交叉是正确的。
使用 IdentityServerAuthenticationHandler
制作自己的 AuthenticationHandler
将是最佳选择。这将允许您使用 DI、拒绝身份验证并在不需要时跳过自定义身份验证处理程序。
首先验证令牌然后添加更多声明的示例AuthenticationHandler
:
public class MyApiAuthenticationHandler : AuthenticationHandler<AuthenticationSchemeOptions>
{
protected override async Task<AuthenticateResult> HandleAuthenticateAsync()
{
// Pass authentication to IdentityServerAuthenticationHandler
var authenticateResult = await Context.AuthenticateAsync("Bearer");
// If token authentication fails, return immediately
if (!authenticateResult.Succeeded)
{
return authenticateResult;
}
// Get user ID from token
var userId = authenticateResult.Principal.Claims
.FirstOrDefault(c => c.Type == JwtClaimTypes.Subject)?.Value;
// Do additional checks for authentication
// e.g. lookup user ID in database
if (userId == null)
{
return AuthenticateResult.NoResult();
}
// Add additional claims
var identity = authenticateResult.Principal.Identity as ClaimsIdentity;
identity.AddClaim(new Claim("MyClaim", "MyValue"));
return authenticateResult;
}
}
向服务添加处理程序:
services.AddAuthentication()
.AddIdentityServerAuthentication(options =>
{
// ...
})
.AddScheme<AuthenticationSchemeOptions, MyApiAuthenticationHandler>("MyApiScheme", null);
现在您可以使用任一方案:
// Authenticate token and get extra API claims
[Authorize(AuthenticationSchemes = "MyApiScheme")]
// Authenticate just the token
[Authorize(AuthenticationSchemes = "Bearer")]
注意 IdentityServerAuthenticationHandler
做同样的事情,using the dotnet JWT handler:
public class IdentityServerAuthenticationHandler : AuthenticationHandler<IdentityServerAuthenticationOptions>
{
protected override async Task<AuthenticateResult> HandleAuthenticateAsync()
{
...
return await Context.AuthenticateAsync(jwtScheme);
...
}
}