IdentityServer4源码颁发token分析及性能优化

时间:2023-12-18 09:27:56

IdentityServer4源码地址

IdentityModel源码地址

  1. 以下的流程用ResourceOwnerPassword类型获取token作为介绍
  2. 分两种获取形式说明
    1. token请求地址为默认TokenEndPoint的地址:"http://demo/connect/token"获取token
    2. 用IdentityModel的TokenClient请求获取token【 性能优化点也在这个地方】

1.默认TokenEndPoint形式

IdentityServer4源码颁发token分析及性能优化

IdentityServer4源码颁发token分析及性能优化

IdentityServer4源码颁发token分析及性能优化

IdentityServer4源码颁发token分析及性能优化

IdentityServer4源码颁发token分析及性能优化

IdentityServer4源码颁发token分析及性能优化


2.TokenClient形式

当请求的Token的地址没有配置默认形式的时候,那就需要利用IdentityModel下的Client来进行请求 举个例子

首先我们需要去获取当前系统的token终结点(tokenEndPoint),然后获取到TokenClient,然后调用RequestResourceOwnerPasswordAsync进行Token颁发

using (var client = new DiscoveryClient(IssuerUri) { Policy = { RequireHttps = false } })

{

res = await client.GetAsync();

}

var tokenClient = new TokenClient(res.TokenEndpoint, clientId, clientSecret);

var tokenResponse = await tokenClient.RequestResourceOwnerPasswordAsync(username, password);

然后我们来看看实际在干什么。我们去看这部分IdentiyModel的源码 (TokenClientExtensions.cs和TokenClient.cs

IdentityServer4源码颁发token分析及性能优化

IdentityServer4源码颁发token分析及性能优化

IdentityServer4源码颁发token分析及性能优化

图1的方法就是我们调用时的方法,可以看到它构造了一下form表单元素就去调用RequestAsync方法。RequestAsync 是使用了HttpClient发起了请求。请求的地址是我们前面获取到的tokenEndPoint的地址,

其实就是进行了一次跳转,问题点就在于这里,绕了一个"圈圈",压测出来效果并不好,为什么不选择直接本地调用方法形式去执行呢? 只需要 仿照 重写一个TokenClient和TokenEndPoint(一直感觉这东西

既然是执handler为什么不叫TokenEndPointHandler) 去把执行方法重写掉,去掉HTTP请求方式,换成直接本地调用方式。这样就直接到类似说明S2步骤

    public class LocalTokenClient : TokenClient
{
private readonly ILocalTokenEndpointHandler _endpointHandler;
public LocalTokenClient(string address, string clientId, string clientSecret, ILocalTokenEndpointHandler endpointHandler, HttpMessageHandler innerHttpMessageHandler = null, AuthenticationStyle style = AuthenticationStyle.BasicAuthentication) : base(address, clientId, clientSecret, innerHttpMessageHandler, style)
{
_endpointHandler = endpointHandler;
} public override async Task<TokenResponse> RequestAsync(IDictionary<string, string> form, CancellationToken cancellationToken = default(CancellationToken))
{
form.Add("client_id", ClientId);
form.Add("client_secret", ClientSecret); try
{
var endpointResult = await _endpointHandler.ProcessAsync(form);
if(endpointResult is TokenResult)
{
TokenResult response = (TokenResult)endpointResult;
if (response != null && response.Response != null)
{
Dictionary<string, object> result = new Dictionary<string, object>();
result.Add("id_token", response.Response.IdentityToken);
result.Add("access_token", response.Response.AccessToken);
result.Add("refresh_token", response.Response.RefreshToken);
result.Add("expires_in", response.Response.AccessTokenLifetime);
result.Add("token_type", OidcConstants.TokenResponse.BearerTokenType); if (!response.Response.Custom.IsNullOrEmpty())
{
foreach (var item in response.Response.Custom)
{
result.Add(item.Key, item.Value);
}
} return new TokenResponse(Newtonsoft.Json.JsonConvert.SerializeObject(result));
}
}
else
{
TokenErrorResult response = (TokenErrorResult)endpointResult;
string error = response.Response.Error;
if(response.Response.ErrorDescription!=null)
{
error = response.Response.ErrorDescription;
}
return new TokenResponse(new Exception(error));
}
}
catch (Exception ex)
{
return new TokenResponse(ex);
} return new TokenResponse(new Exception("request token failed"));
}
}
    public interface ILocalTokenEndpointHandler
{
/// <summary>
/// Processes the request.
/// </summary>
/// <param name="context">The HTTP context.</param>
/// <returns></returns>
Task<IEndpointResult> ProcessAsync(IDictionary<string, string> form);
} public class LocalTokenEndpoint : ILocalTokenEndpointHandler
{
private readonly ICustomClientSecretValidator _clientValidator;
private readonly ITokenRequestValidator _requestValidator;
private readonly ITokenResponseGenerator _responseGenerator;
private readonly IEventService _events;
private readonly ILogger _logger;
private readonly IClientStore _clients; /// <summary>
/// Initializes a new instance of the <see cref="TokenEndpoint" /> class.
/// </summary>
/// <param name="clientValidator">The client validator.</param>
/// <param name="requestValidator">The request validator.</param>
/// <param name="responseGenerator">The response generator.</param>
/// <param name="events">The events.</param>
/// <param name="logger">The logger.</param>
public LocalTokenEndpoint(
ICustomClientSecretValidator clientValidator,
ITokenRequestValidator requestValidator,
ITokenResponseGenerator responseGenerator,
IEventService events,
ILogger<LocalTokenEndpoint> logger,
IClientStore clients)
{
_clientValidator = clientValidator;
_requestValidator = requestValidator;
_responseGenerator = responseGenerator;
_events = events;
_logger = logger;
_clients = clients;
} /// <summary>
/// Processes the request.
/// </summary>
/// <param name="context">The HTTP context.</param>
/// <returns></returns>
public async Task<IEndpointResult> ProcessAsync(IDictionary<string, string> form)
{
_logger.LogTrace("Processing token request."); return await ProcessTokenRequestAsync(form);
} private Task RaiseSuccessEventAsync(string clientId, string authMethod)
{
return _events.RaiseAsync(new ClientAuthenticationSuccessEvent(clientId, authMethod));
} private async Task<IEndpointResult> ProcessTokenRequestAsync(IDictionary<string, string> form)
{
_logger.LogDebug("Start token request.");
// validate client
var clientResult = await _clientValidator.ValidateAsync(form);
if (clientResult.Client == null)
{
return Error(OidcConstants.TokenErrors.InvalidClient);
} // validate request
var form2 = form.AsNameValueCollection();
_logger.LogTrace("Calling into token request validator: {type}", _requestValidator.GetType().FullName);
var requestResult = await _requestValidator.ValidateRequestAsync(form2, clientResult); if (requestResult.IsError)
{
await _events.RaiseAsync(new TokenIssuedFailureEvent(requestResult));
return Error(requestResult.Error, requestResult.ErrorDescription, requestResult.CustomResponse);
} // create response
_logger.LogTrace("Calling into token request response generator: {type}", _responseGenerator.GetType().FullName);
var response = await _responseGenerator.ProcessAsync(requestResult); await _events.RaiseAsync(new TokenIssuedSuccessEvent(response, requestResult));
LogTokens(response, requestResult); // return result
_logger.LogDebug("Token request success.");
return new TokenResult(response);
} private TokenErrorResult Error(string error, string errorDescription = null, Dictionary<string, object> custom = null)
{
var response = new TokenErrorResponse
{
Error = error,
ErrorDescription = errorDescription,
Custom = custom
}; return new TokenErrorResult(response);
} private void LogTokens(TokenResponse response, TokenRequestValidationResult requestResult)
{
var clientId = $"{requestResult.ValidatedRequest.Client.ClientId} ({requestResult.ValidatedRequest.Client?.ClientName ?? "no name set"})";
var subjectId = requestResult.ValidatedRequest.Subject?.GetSubjectId() ?? "no subject"; if (response.IdentityToken != null)
{
_logger.LogTrace("Identity token issued for {clientId} / {subjectId}: {token}", clientId, subjectId, response.IdentityToken);
}
if (response.RefreshToken != null)
{
_logger.LogTrace("Refresh token issued for {clientId} / {subjectId}: {token}", clientId, subjectId, response.RefreshToken);
}
if (response.AccessToken != null)
{
_logger.LogTrace("Access token issued for {clientId} / {subjectId}: {token}", clientId, subjectId, response.AccessToken);
}
}
}