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