我有一个架构,我有一个初始的ASP MVC登陆页面,它调用一个Web API服务,将调用转换为另外两个本身也有的。“/ p>
目前通过Windows身份验证用户用户/角色处理身份验证。
我想在到达aspmvc方面时获取身份服务器令牌(仍使用Windows身份验证)然后返回一个具有合适声明/范围的令牌,我可以通过提取和传递所有后续调用来重用它线。
这可能吗?这里的首选或最佳做法是什么?也许我会使用服务器来为每个跳跃服务器流..但似乎跟着获得另一个令牌..我甚至会把它们放在内部玩偶中?
答案 0 :(得分:5)
更新 - 在与Matt G讨论后,我在答案中添加了更好的解释,以便明确我的观点。我估计一开始我还不够清楚。
更新2 - 添加第5点
我认为应该为一个客户端发出一个令牌,并且必须仅由该特定客户端使用才能访问它要求访问的所有资源。
<强>案例强>
<强>评论强>
这意味着Api2可以访问Api3,Api4,Api5。但是如果Api2不能被授予Api5的访问权限会怎样?现在你有问题了。一旦出现这种情况,您就必须重新设计安全机制。
此外,这意味着发送到Api2的令牌包含与其无关的范围,这对我来说听起来有点奇怪。
另一方面,Api1的范围可能意味着Api2的不同可能导致误解。但这取决于你的发展。
如果您使用范围执行身份验证和授权,则您不应该共享您的令牌,因为Api1可以执行代码,例如Api2不应该&#39 ; t执行,这是一个安全问题。
如果Api1是向IdP请求令牌的人。 Api2会发生什么事 你想与Api1分开使用吗?它不能打电话给别人Apis,因为Api1没有通过令牌?或者所有的Apis都有能力向IdP请求令牌,并且所有人都将令牌传递给其他人Apis,这取决于Api第一次打电话的时间?你可能比需要更复杂吗?
你想要达到的目标是可行的,但对我而言,这不是一个好主意。
下面我建议你解决这个问题的替代方案。
听起来你需要一个TokenCache和一个机制来在每次HttpClient.Send时注入它。这就是我的建议。
你应该创建一个名为TokenCache的类,这个类负责每次到期时获取令牌,无效或为空。
public class TokenCache : ITokenCache
{
public TokenClient TokenClient { get; set; }
private readonly string _scope;
private DateTime _tokenCreation;
private TokenResponse _tokenResponse;
public TokenCache(string scope)
{
_scope = scope;
}
private bool IsTokenValid()
{
return _tokenResponse != null &&
!_tokenResponse.IsError &&
!string.IsNullOrWhiteSpace(_tokenResponse.AccessToken) &&
(_tokenCreation.AddSeconds(_tokenResponse.ExpiresIn) > DateTime.UtcNow);
}
private async Task RequestToken()
{
_tokenResponse = await TokenClient.RequestClientCredentialsAsync(_scope).ConfigureAwait(false);
_tokenCreation = DateTime.UtcNow;
}
public async Task<string> GetAccessToken(bool forceRefresh = false)
{
if (!forceRefresh && IsTokenValid()) return _tokenResponse.AccessToken;
await RequestToken().ConfigureAwait(false);
if (!IsTokenValid())
{
throw new InvalidOperationException("An unexpected token validation error has occured during a token request.");
}
return _tokenResponse.AccessToken;
}
}
您创建了一个TokenHttpHandler类,如下所示。每次执行HttpClient.Send时,此类都将设置Bearer令牌。请注意,我们使用TokenCache(_tokenCache.GetAccessToken)来获取SetAuthHeaderAndSendAsync方法中的标记。通过这种方式,您可以确保每次从api / mvc应用程序拨打电话到另一个api时都会发送令牌。
public class TokenHttpHandler : DelegatingHandler
{
private readonly ITokenCache _tokenCache;
public TokenHttpHandler(ITokenCache tokenCache)
{
InnerHandler = new HttpClientHandler();
_tokenCache = tokenCache;
}
protected override async Task<HttpResponseMessage> SendAsync(HttpRequestMessage request, CancellationToken cancellationToken)
{
var response = await SetAuthHeaderAndSendAsync(request, cancellationToken, false).ConfigureAwait(false);
//check for 401 and retry
if (response.StatusCode == HttpStatusCode.Unauthorized)
{
response = await SetAuthHeaderAndSendAsync(request, cancellationToken, true);
}
return response;
}
private async Task<HttpResponseMessage> SetAuthHeaderAndSendAsync(HttpRequestMessage request, CancellationToken cancellationToken, bool forceTokenRefresh)
{
request.Headers.Authorization = new AuthenticationHeaderValue("Bearer", await _tokenCache.GetAccessToken(forceTokenRefresh).ConfigureAwait(false));
return await base.SendAsync(request, cancellationToken).ConfigureAwait(false);
}
然后在ExtendedHttpClient中使用它,如下所示。请注意,我们正在将TokenHttpHandler注入构造函数。
public class ExtendedHttpClient : HttpClient
{
public ExtendedHttpClient(TokenHttpHandler messageHandler) : base(messageHandler)
{
DefaultRequestHeaders.Accept.Add(new MediaTypeWithQualityHeaderValue("application/json"));
DefaultRequestHeaders.AcceptEncoding.Add(new StringWithQualityHeaderValue("gzip"));
}
}
最后,在您的IoC配置中,您需要添加新类。
如果您想将上述代码重用于多个MVC应用程序/ Api,那么您应将其放在共享库(例如基础架构)中,然后仅为每个IdentityServer客户端配置IoC。
builder.RegisterType<TokenHttpHandler>().AsSelf();
builder.RegisterType<ExtendedHttpClient>().As<HttpClient>();
builder.RegisterType<TokenCache>()
.As<ITokenCache>()
.WithParameter("scope", "YOUR_SCOPES")
.OnActivating(e => e.Instance.TokenClient = e.Context.Resolve<TokenClient>())
.SingleInstance();
builder.Register(context =>
{
var address = "YOUR_AUTHORITY";
return new TokenClient(address, "ClientID", "Secret");
})
.AsSelf();
请注意,此示例使用ClientCredentials流程,但您可以采用此概念并对其进行修改以使其符合您的要求。
希望它有所帮助。 亲切的问候 丹尼尔
答案 1 :(得分:1)
这是Microserices architecure的常见问题,它通过API getway模式处理。 应在API网关级别处理所有令牌验证。在令牌验证之后,请求应转发到(微)服务,该服务可以信任该请求。如果您有任何关于令牌安全性的更新/修复/改进/添加的内容,则可以在一个地方完成。
答案 2 :(得分:1)
穷人的代表团 - 在随后的API调用中简单地转发相同的承载令牌。正如其他评论所述,这引入了范围的差异。
扩展授权 - Identity Server 4引入了此授权类型以支持委派。提供持有者令牌以换取新令牌以调用第二个API。
答案 3 :(得分:0)
我认为你是对的,你只需传递令牌。显然,令牌需要它可能遇到的所有api的范围。 MVC应用程序拥有令牌并将其作为承载发送给api,api可以简单地将相同的承载令牌重新发送到它消耗的任何api,等等......