如何调用使用令牌 OAuth 的第三方 API,以允许使用依赖关系注入通过 Web API 进行调用



我希望开始将我拥有的项目从一堆类迁移到 DI 模式,在那里我可以注入对象并使我的项目可测试。

我需要调用第三方 API 来处理他们的一些身份验证(我使用多个第三方 API(,我做的一件事是处理持有者令牌 (OAuth(,我想知道如何或什么是处理具有到期日期的 OAuth 令牌或持有者令牌的最佳方法。

最初,我使用了一个带有静态成员和静态函数的类来存储令牌(24 小时过期(,如果它没有过期,则无需获取它,只需使用变量中的持有者令牌即可。

通过 DI 遵守此类令牌请求和响应的最佳方法是什么? 我想在服务器端执行此操作,这将是一个 angular 或 jquery 将与之交互的 Web API。 .NET 框架标准。

我想补充一点,目前我正在使用 Unity 进行 DI。

您不能只使用一个类来管理通过 DI 注册为单例的内容吗? 然后实际上会和你的旧静态东西一样。

(我假设这是为了您的服务器和其他服务器之间的通信,并且不直接涉及您的 api 客户端(

如果你不喜欢让某种臃肿的单例一直漂浮的想法,你可以简单地使用这样的东西来抽象出令牌的存储

public interface ITokenStore
{
string GetCurrentToken();
void SetToken(string token);
}
public class TokenStore : ITokenStore
{
private DateTime _tokenRefreshedAt;
private string _currentToken;
public string GetCurrentToken()
{
//if we last got the token more than 23 hours ago,
//just reset token
if (lastTokenRefreshed.AddHours(23) < DateTime.Now)
{
_currentToken = null;
}
return _currentToken;        
}
public void SetCurrentToken(string token)
{
_currentToken = token;
}
}

然后将其注册为单例(不熟悉 Unity,因此请调整语法以适应(:

container.RegisterSingleton<ITokenStore, TokenStore>();

然后,需要令牌的服务可以使用每个请求或暂时生存期进行注册,只需执行以下操作:

class SomeService
{
private ITokenStore _tokenStore;
public SomeService(ITokenStore tokenStore)
{
_tokenStore = tokenStore;
}
public string DoThings(params..)
{
var currentToken = _tokenStore.GetCurrentToken();
if (currentToken == null)
{
currentToken = GetNewTokenSomehow();
_tokenStore.SetCurrentToken(currentToken);          
}
.... Do other things....
}
}

你可以让 tokenstore 类本身来获取一个新令牌,但如果它的生命周期是单例的,那么你注入到其中的任何服务也必须是单例,所以我可能有一个每个请求生命周期的 TokenManager,它处理所有这些,但它本身使用单例令牌存储或其他东西......

Unity作为 DI 框架不再由 Microsoft 维护,现在负责社区,请参阅此处的链接: Unity Future

现在,如果要将项目迁移到新的webapi,请开始寻找Aspnet核心:ASPNet核心

现在就令牌而言,您可以开始寻找与Identity Server的集成,它是OAuth和OpenId的实现,并且与AspNet Core AspNet Core Security Video集成。在与身份提供商(可以是Google,Facebook等(通信时,您无需随时存储令牌,如果要刷新令牌,可以自行处理。 请参阅以下示例:

public interface IApplicationHttpClient
{
Task<HttpClient> GetClient();
}
public class ApplicationHttpClient : IApplicationHttpClient
{
private readonly IHttpContextAccessor _httpContextAccessor;
private HttpClient _httpClient = new HttpClient();
public ApplicationHttpClient(IHttpContextAccessor httpContextAccessor)
{
_httpContextAccessor = httpContextAccessor;
}
public async Task<HttpClient> GetClient()
{
string accessToken = string.Empty;
// get the current HttpContext to access the tokens
var currentContext = _httpContextAccessor.HttpContext;
// get access token
//accessToken = await currentContext.GetTokenAsync(OpenIdConnectParameterNames.AccessToken);
//should we renew access & refresh tokens?
//get expires_at value
var expires_at = await currentContext.GetTokenAsync("expires_at");
//compare -make sure to use the exact date formats for comparison(UTC, in this case)
if (string.IsNullOrWhiteSpace(expires_at) ||
((DateTime.Parse(expires_at).AddSeconds(-60)).ToUniversalTime() < DateTime.UtcNow))
{
accessToken = await RenewTokens();
}
else
{
//get access token
accessToken = await currentContext.GetTokenAsync(OpenIdConnectParameterNames.AccessToken);
}
if (!string.IsNullOrWhiteSpace(accessToken))
{
// set as Bearer token
_httpClient.SetBearerToken(accessToken);
}
//api url
_httpClient.BaseAddress = new Uri("https://localhost:44310/");
_httpClient.DefaultRequestHeaders.Accept.Clear();
_httpClient.DefaultRequestHeaders.Accept.Add(
new MediaTypeWithQualityHeaderValue("application/json"));
return _httpClient;
}
public async Task<string> RenewTokens()
{
//get the current HttpContext to access the tokens
var currentContext = _httpContextAccessor.HttpContext;
//get the metadata from the IDP
var discoveryClient = new DiscoveryClient("https://localhost:44329/");
var metaDataResponse = await discoveryClient.GetAsync();
//create a new token client to get new tokens
var tokenClient = new TokenClient(metaDataResponse.TokenEndpoint, "mywebapp", "secret");
//get the saved refresh token
var currentRefreshToken = await currentContext.GetTokenAsync(OpenIdConnectParameterNames.RefreshToken);
//refresh the tokens
var tokenResult = await tokenClient.RequestRefreshTokenAsync(currentRefreshToken);
if (!tokenResult.IsError)
{
var updatedTokens = new List<AuthenticationToken>();
updatedTokens.Add(new AuthenticationToken
{
Name = OpenIdConnectParameterNames.IdToken,
Value = tokenResult.IdentityToken
});
updatedTokens.Add(new AuthenticationToken
{
Name = OpenIdConnectParameterNames.AccessToken,
Value = tokenResult.AccessToken
});
updatedTokens.Add(new AuthenticationToken
{
Name = OpenIdConnectParameterNames.RefreshToken,
Value = tokenResult.RefreshToken
});
var expiresAt = DateTime.UtcNow + TimeSpan.FromSeconds(tokenResult.ExpiresIn);
updatedTokens.Add(new AuthenticationToken
{
Name = "expires-at",
Value = expiresAt.ToString("o", CultureInfo.InvariantCulture)
});
//get authenticate result, containing the current principal & properties
var currentAuthenticateResult = await currentContext.AuthenticateAsync("Cookies");
//store the updated tokens
currentAuthenticateResult.Properties.StoreTokens(updatedTokens);
//sign in
await currentContext.SignInAsync("Cookies", currentAuthenticateResult.Principal,
currentAuthenticateResult.Properties);
//return the new access token
return tokenResult.AccessToken;
}
throw new Exception("Problem encountered while refreshing tokens.", tokenResult.Exception);
}
}

最新更新