使用没有密码的IdentityServer4生成访问令牌
Generate access token with IdentityServer4 without password
我已经使用 ROPC 流创建了 ASP.NET 受 IdentityServer4 保护的核心 WebApi(使用此示例:https://github.com/robisim74/AngularSPAWebAPI)。
如何在没有密码的情况下从服务器手动生成access_token?
进一步补充我对你原来问题的评论。在 implicit/hybrid 流程中实施模拟功能。如果确定用户是 "super admin",则在身份验证后向他们提供一个额外的步骤,让他们 enter/select 他们希望模拟的帐户。一旦完成,只需在身份服务器上作为选定用户建立会话(并可能存储额外的声明,表明它是一个模拟会话以及谁在进行模拟)。然后将发出任何令牌,就好像您是该用户一样,而无需知道密码。
此外,如果您想自己创建令牌,请查看 IdSrv4 提供的 ITokenCreationService。您可以将其注入您自己的 controller/service/whatever 并使用 CreateTokenAsync(Token token) 生成带有您喜欢的任何声明的签名 JWT。
[HttpPost("loginas/{id}")]
[Authorize(Roles = "admin")]
public async Task<IActionResult> LoginAs(int id, [FromServices] ITokenService TS,
[FromServices] IUserClaimsPrincipalFactory<ApplicationUser> principalFactory,
[FromServices] IdentityServerOptions options)
{
var Request = new TokenCreationRequest();
var User = await userManager.FindByIdAsync(id.ToString());
var IdentityPricipal = await principalFactory.CreateAsync(User);
var IdServerPrincipal = IdentityServerPrincipal.Create(User.Id.ToString(), User.UserName, IdentityPricipal.Claims.ToArray());
Request.Subject = IdServerPrincipal;
Request.IncludeAllIdentityClaims = true;
Request.ValidatedRequest = new ValidatedRequest();
Request.ValidatedRequest.Subject = Request.Subject;
Request.ValidatedRequest.SetClient(Config.GetClients().First());
Request.Resources = new Resources(Config.GetIdentityResources(), Config.GetApiResources());
Request.ValidatedRequest.Options = options;
Request.ValidatedRequest.ClientClaims = IdServerPrincipal.Claims.ToArray();
var Token = await TS.CreateAccessTokenAsync(Request);
Token.Issuer = "http://" + HttpContext.Request.Host.Value;
var TokenValue = await TS.CreateSecurityTokenAsync(Token);
return Ok(TokenValue);
}
对于新发布的 IdentityServer 2.0.0,代码需要一些修改:
[HttpPost("loginas/{id}")]
[Authorize(Roles = "admin")]
public async Task<IActionResult> LoginAs(int id, [FromServices] ITokenService TS,
[FromServices] IUserClaimsPrincipalFactory<ApplicationUser> principalFactory,
[FromServices] IdentityServerOptions options)
{
var Request = new TokenCreationRequest();
var User = await userManager.FindByIdAsync(id.ToString());
var IdentityPricipal = await principalFactory.CreateAsync(User);
var IdentityUser = new IdentityServerUser(User.Id.ToString());
IdentityUser.AdditionalClaims = IdentityPricipal.Claims.ToArray();
IdentityUser.DisplayName = User.UserName;
IdentityUser.AuthenticationTime = System.DateTime.UtcNow;
IdentityUser.IdentityProvider = IdentityServerConstants.LocalIdentityProvider;
Request.Subject = IdentityUser.CreatePrincipal();
Request.IncludeAllIdentityClaims = true;
Request.ValidatedRequest = new ValidatedRequest();
Request.ValidatedRequest.Subject = Request.Subject;
Request.ValidatedRequest.SetClient(Config.GetClients().First());
Request.Resources = new Resources(Config.GetIdentityResources(), Config.GetApiResources());
Request.ValidatedRequest.Options = options;
Request.ValidatedRequest.ClientClaims = IdentityUser.AdditionalClaims;
var Token = await TS.CreateAccessTokenAsync(Request);
Token.Issuer = HttpContext.Request.Scheme + "://" + HttpContext.Request.Host.Value;
var TokenValue = await TS.CreateSecurityTokenAsync(Token);
return Ok(TokenValue);
}
使用这个:
http://docs.identityserver.io/en/latest/topics/tools.html
使用身份服务器附带的工具:
在构造函数中声明,通过依赖注入接收。
IdentityServer4.IdentityServerTools_identityServerTools
var issuer = "http://" + httpRequest.Host.Value;
var token = await _identityServerTools.IssueJwtAsync(
30000,
issuer,
new System.Security.Claims.Claim[1]
{
new System.Security.Claims.Claim("cpf", cpf)
}
);
这是实现此目的的另一种方法:
首先创建一个名为 loginBy 的自定义授权
public class LoginByGrant : ICustomGrantValidator
{
private readonly ApplicationUserManager _userManager;
public string GrantType => "loginBy";
public LoginByGrant(ApplicationUserManager userManager)
{
_userManager = userManager;
}
public async Task<CustomGrantValidationResult> ValidateAsync(ValidatedTokenRequest request)
{
var userId = Guid.Parse(request.Raw.Get("user_id"));
var user = await _userManager.FindByIdAsync(userId);
if (user == null)
return await Task.FromResult<CustomGrantValidationResult>(new CustomGrantValidationResult("user not exist"));
var userClaims = await _userManager.GetClaimsAsync(user.Id);
return
await Task.FromResult<CustomGrantValidationResult>(new CustomGrantValidationResult(user.Id.ToString(), "custom", userClaims));
}
}
然后在身份启动中添加此自定义授权 class
factory.CustomGrantValidators.Add(
new Registration<ICustomGrantValidator>(resolver => new LoginByGrant(ApplicaionUserManager)));
最后在你的 api
public async Task<IHttpActionResult> LoginBy(Guid userId)
{
var tokenClient = new TokenClient(Constants.TokenEndPoint, Constants.ClientId, Constants.Secret);
var payload = new { user_id = userId.ToString() };
var result = await tokenClient.RequestCustomGrantAsync("loginBy", "customScope", payload);
if (result.IsError)
return Ok(result.Json);
return Ok(new { access_token = result.AccessToken, expires_in = result.ExpiresIn});
}
回答有点晚了。
在我的 Generating Access Token Without Password
案例中,还有另一个 identity server
作为组织 sso,我们的实现已经使用了 IdentityServer
,因此我们需要从第二个 [=15] 获取用户令牌=] (用户登录并重定向到我们的应用程序后),提取 sub
,检查它是否已经存在(如果不插入到我们的本地 IdentityServer
),最后 select 用户并使用新的授予为用户获取令牌。
您的客户应该将此 granttype
作为允许的授权类型(此处 userexchange
):
有关详细信息,请参阅:identity server docs, or duende docs
public class TokenExchangeGrantValidator : IExtensionGrantValidator {
protected readonly UserManager<ToranjApplicationUser> _userManager;
private readonly IEventService _events;
public TokenExchangeGrantValidator(ITokenValidator validator, IHttpContextAccessor httpContextAccessor, UserManager<ToranjApplicationUser> userManager
, IEventService events) {
_userManager = userManager;
_events = events;
}
public async Task ValidateAsync(ExtensionGrantValidationContext context) {
var userName = context.Request.Raw.Get("uname");
if (string.IsNullOrEmpty(userName)) {
context.Result = new GrantValidationResult(TokenRequestErrors.InvalidGrant);
return;
}
var user = await _userManager.FindByNameAsync(userName);
// or use this one, if you are sending userId
//var user = await _userManager.FindByIdAsync(userId);
if (null == user) {
context.Result = new GrantValidationResult(TokenRequestErrors.InvalidGrant);
return;
}
await _events.RaiseAsync(new UserLoginSuccessEvent(user.UserName, user.Id.ToString(), user.UserName, false, context.Request.ClientId));
var customResponse = new Dictionary<string, object>
{
{OidcConstants.TokenResponse.IssuedTokenType, OidcConstants.TokenTypeIdentifiers.AccessToken}
};
context.Result = new GrantValidationResult(
subject: user.Id.ToString(),
authenticationMethod: GrantType,
customResponse: customResponse);
}
public string GrantType => "userexchange";
}
在 startup
的 ConfigureServices
之后 var builder = services.AddIdentityServer(...)
添加您新创建的 class。
builder.AddExtensionGrantValidator<TokenExchangeGrantValidator>();
调用它获取令牌非常简单:
POST /connect/token
grant_type=userexchange&
scope=yourapi&
uname=yourusername&
client_id=yourClientId
client_secret=secret
我已经使用 ROPC 流创建了 ASP.NET 受 IdentityServer4 保护的核心 WebApi(使用此示例:https://github.com/robisim74/AngularSPAWebAPI)。
如何在没有密码的情况下从服务器手动生成access_token?
进一步补充我对你原来问题的评论。在 implicit/hybrid 流程中实施模拟功能。如果确定用户是 "super admin",则在身份验证后向他们提供一个额外的步骤,让他们 enter/select 他们希望模拟的帐户。一旦完成,只需在身份服务器上作为选定用户建立会话(并可能存储额外的声明,表明它是一个模拟会话以及谁在进行模拟)。然后将发出任何令牌,就好像您是该用户一样,而无需知道密码。
此外,如果您想自己创建令牌,请查看 IdSrv4 提供的 ITokenCreationService。您可以将其注入您自己的 controller/service/whatever 并使用 CreateTokenAsync(Token token) 生成带有您喜欢的任何声明的签名 JWT。
[HttpPost("loginas/{id}")]
[Authorize(Roles = "admin")]
public async Task<IActionResult> LoginAs(int id, [FromServices] ITokenService TS,
[FromServices] IUserClaimsPrincipalFactory<ApplicationUser> principalFactory,
[FromServices] IdentityServerOptions options)
{
var Request = new TokenCreationRequest();
var User = await userManager.FindByIdAsync(id.ToString());
var IdentityPricipal = await principalFactory.CreateAsync(User);
var IdServerPrincipal = IdentityServerPrincipal.Create(User.Id.ToString(), User.UserName, IdentityPricipal.Claims.ToArray());
Request.Subject = IdServerPrincipal;
Request.IncludeAllIdentityClaims = true;
Request.ValidatedRequest = new ValidatedRequest();
Request.ValidatedRequest.Subject = Request.Subject;
Request.ValidatedRequest.SetClient(Config.GetClients().First());
Request.Resources = new Resources(Config.GetIdentityResources(), Config.GetApiResources());
Request.ValidatedRequest.Options = options;
Request.ValidatedRequest.ClientClaims = IdServerPrincipal.Claims.ToArray();
var Token = await TS.CreateAccessTokenAsync(Request);
Token.Issuer = "http://" + HttpContext.Request.Host.Value;
var TokenValue = await TS.CreateSecurityTokenAsync(Token);
return Ok(TokenValue);
}
对于新发布的 IdentityServer 2.0.0,代码需要一些修改:
[HttpPost("loginas/{id}")]
[Authorize(Roles = "admin")]
public async Task<IActionResult> LoginAs(int id, [FromServices] ITokenService TS,
[FromServices] IUserClaimsPrincipalFactory<ApplicationUser> principalFactory,
[FromServices] IdentityServerOptions options)
{
var Request = new TokenCreationRequest();
var User = await userManager.FindByIdAsync(id.ToString());
var IdentityPricipal = await principalFactory.CreateAsync(User);
var IdentityUser = new IdentityServerUser(User.Id.ToString());
IdentityUser.AdditionalClaims = IdentityPricipal.Claims.ToArray();
IdentityUser.DisplayName = User.UserName;
IdentityUser.AuthenticationTime = System.DateTime.UtcNow;
IdentityUser.IdentityProvider = IdentityServerConstants.LocalIdentityProvider;
Request.Subject = IdentityUser.CreatePrincipal();
Request.IncludeAllIdentityClaims = true;
Request.ValidatedRequest = new ValidatedRequest();
Request.ValidatedRequest.Subject = Request.Subject;
Request.ValidatedRequest.SetClient(Config.GetClients().First());
Request.Resources = new Resources(Config.GetIdentityResources(), Config.GetApiResources());
Request.ValidatedRequest.Options = options;
Request.ValidatedRequest.ClientClaims = IdentityUser.AdditionalClaims;
var Token = await TS.CreateAccessTokenAsync(Request);
Token.Issuer = HttpContext.Request.Scheme + "://" + HttpContext.Request.Host.Value;
var TokenValue = await TS.CreateSecurityTokenAsync(Token);
return Ok(TokenValue);
}
使用这个:
http://docs.identityserver.io/en/latest/topics/tools.html
使用身份服务器附带的工具:
在构造函数中声明,通过依赖注入接收。
IdentityServer4.IdentityServerTools_identityServerTools
var issuer = "http://" + httpRequest.Host.Value; var token = await _identityServerTools.IssueJwtAsync( 30000, issuer, new System.Security.Claims.Claim[1] { new System.Security.Claims.Claim("cpf", cpf) } );
这是实现此目的的另一种方法:
首先创建一个名为 loginBy 的自定义授权
public class LoginByGrant : ICustomGrantValidator
{
private readonly ApplicationUserManager _userManager;
public string GrantType => "loginBy";
public LoginByGrant(ApplicationUserManager userManager)
{
_userManager = userManager;
}
public async Task<CustomGrantValidationResult> ValidateAsync(ValidatedTokenRequest request)
{
var userId = Guid.Parse(request.Raw.Get("user_id"));
var user = await _userManager.FindByIdAsync(userId);
if (user == null)
return await Task.FromResult<CustomGrantValidationResult>(new CustomGrantValidationResult("user not exist"));
var userClaims = await _userManager.GetClaimsAsync(user.Id);
return
await Task.FromResult<CustomGrantValidationResult>(new CustomGrantValidationResult(user.Id.ToString(), "custom", userClaims));
}
}
然后在身份启动中添加此自定义授权 class
factory.CustomGrantValidators.Add(
new Registration<ICustomGrantValidator>(resolver => new LoginByGrant(ApplicaionUserManager)));
最后在你的 api
public async Task<IHttpActionResult> LoginBy(Guid userId)
{
var tokenClient = new TokenClient(Constants.TokenEndPoint, Constants.ClientId, Constants.Secret);
var payload = new { user_id = userId.ToString() };
var result = await tokenClient.RequestCustomGrantAsync("loginBy", "customScope", payload);
if (result.IsError)
return Ok(result.Json);
return Ok(new { access_token = result.AccessToken, expires_in = result.ExpiresIn});
}
回答有点晚了。
在我的 Generating Access Token Without Password
案例中,还有另一个 identity server
作为组织 sso,我们的实现已经使用了 IdentityServer
,因此我们需要从第二个 [=15] 获取用户令牌=] (用户登录并重定向到我们的应用程序后),提取 sub
,检查它是否已经存在(如果不插入到我们的本地 IdentityServer
),最后 select 用户并使用新的授予为用户获取令牌。
您的客户应该将此 granttype
作为允许的授权类型(此处 userexchange
):
有关详细信息,请参阅:identity server docs, or duende docs
public class TokenExchangeGrantValidator : IExtensionGrantValidator {
protected readonly UserManager<ToranjApplicationUser> _userManager;
private readonly IEventService _events;
public TokenExchangeGrantValidator(ITokenValidator validator, IHttpContextAccessor httpContextAccessor, UserManager<ToranjApplicationUser> userManager
, IEventService events) {
_userManager = userManager;
_events = events;
}
public async Task ValidateAsync(ExtensionGrantValidationContext context) {
var userName = context.Request.Raw.Get("uname");
if (string.IsNullOrEmpty(userName)) {
context.Result = new GrantValidationResult(TokenRequestErrors.InvalidGrant);
return;
}
var user = await _userManager.FindByNameAsync(userName);
// or use this one, if you are sending userId
//var user = await _userManager.FindByIdAsync(userId);
if (null == user) {
context.Result = new GrantValidationResult(TokenRequestErrors.InvalidGrant);
return;
}
await _events.RaiseAsync(new UserLoginSuccessEvent(user.UserName, user.Id.ToString(), user.UserName, false, context.Request.ClientId));
var customResponse = new Dictionary<string, object>
{
{OidcConstants.TokenResponse.IssuedTokenType, OidcConstants.TokenTypeIdentifiers.AccessToken}
};
context.Result = new GrantValidationResult(
subject: user.Id.ToString(),
authenticationMethod: GrantType,
customResponse: customResponse);
}
public string GrantType => "userexchange";
}
在 startup
的 ConfigureServices
之后 var builder = services.AddIdentityServer(...)
添加您新创建的 class。
builder.AddExtensionGrantValidator<TokenExchangeGrantValidator>();
调用它获取令牌非常简单:
POST /connect/token
grant_type=userexchange&
scope=yourapi&
uname=yourusername&
client_id=yourClientId
client_secret=secret