IdentityModel源碼地址github
1.默認TokenEndPoint形式 性能優化
2.TokenClient形式async
當請求的Token的地址沒有配置默認形式的時候,那就須要利用IdentityModel下的Client來進行請求 舉個例子ide
首先咱們須要去獲取當前系統的token終結點(tokenEndPoint),而後獲取到TokenClient,而後調用RequestResourceOwnerPasswordAsync進行Token頒發性能
using (var client = new DiscoveryClient(IssuerUri) { Policy = { RequireHttps = false } }) 優化
{ ui
res = await client.GetAsync(); spa
} code
var tokenClient = new TokenClient(res.TokenEndpoint, clientId, clientSecret);
var tokenResponse = await tokenClient.RequestResourceOwnerPasswordAsync(username, password);
而後咱們來看看實際在幹什麼。咱們去看這部分IdentiyModel的源碼 (TokenClientExtensions.cs和TokenClient.cs)
圖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); } } }