그래서 나는 내가 원하는 정확히 작동하는 해결책을 발견했습니다. 더 나은 해결책이 있다면 언제든지 게시 해주십시오.
해결책 : VssClientCredentials.Storage
속성에 IVssCredentialStorage
을 구현하는 클래스가 필요하므로 VssClientCredentialStorage
클래스에서 파생하여 해당 인터페이스를 구현하는 클래스를 만들었습니다.
그런 다음 레지스트리에 토큰과 함께 저장된 만료리스를 기반으로 토큰을 검색하고 제거하는 방법을 재정의합니다.
토큰이 검색되어 만료 된 임대 기간이있는 경우 토큰이 저장소에서 제거되고 null이 반환되고 VssConnection
클래스에 UI가 표시되어 사용자가 자격 증명을 입력하도록합니다. 토큰이 만료되지 않으면 사용자에게 프롬프트되지 않고 캐시 된 신임이 사용됩니다.
그래서 지금은 다음을 수행 할 수 있습니다
- 명령 줄에서 다시 프롬프트 VSTS 클라이언트
- 실행 유틸리티에 처음으로 명령 줄에서
- 공급 자격 증명을 내 유틸리티를 호출 묻지도 않고!
이제 유틸리티에 표준 임대 만료가 설정되었지만 사용자가 명령 줄 옵션을 사용하여 변경할 수 있습니다. 또한 사용자는 캐시 된 자격 증명도 지울 수 있습니다.
키가 RemoveToken 무시에 있습니다. 기본 클래스에 대한 호출은 레지스트리에서 제거하는 것이므로, 내 경우에는 임대가 만료되지 않은 경우이를 건너 뛰면 레지스트리 항목이 유지됩니다. 이를 통해 VssConnection은 캐시 된 자격 증명을 사용하고 프로그램이 실행될 때마다 사용자에게 메시지를 표시하지 않습니다!호출 코드의
예 :
public static VssConnection CreateConnection(Uri url, VssCredentials credentials = null, double tokenLeaseInSeconds = VssClientCredentialCachingStorage.DefaultTokenLeaseInSeconds)
{
credentials = credentials ?? new VssClientCredentials();
credentials.Storage = GetVssClientCredentialStorage(tokenLeaseInSeconds);
var connection = new VssConnection(url, credentials);
connection.ConnectAsync().SyncResult();
return connection;
}
private static VssClientCredentialCachingStorage GetVssClientCredentialStorage(double tokenLeaseInSeconds)
{
return new VssClientCredentialCachingStorage("YourApp", "YourNamespace", tokenLeaseInSeconds);
}
가 파생 스토리지 클래스을 :
/// <summary>
/// Class to alter the credential storage behavior to allow the token to be cached between sessions.
/// </summary>
/// <seealso cref="Microsoft.VisualStudio.Services.Common.IVssCredentialStorage" />
public class VssClientCredentialCachingStorage : VssClientCredentialStorage
{
#region [Private]
private const string __tokenExpirationKey = "ExpirationDateTime";
private double _tokenLeaseInSeconds;
#endregion [Private]
/// <summary>
/// The default token lease in seconds
/// </summary>
public const double DefaultTokenLeaseInSeconds = 86400;// one day
/// <summary>
/// Initializes a new instance of the <see cref="VssClientCredentialCachingStorage"/> class.
/// </summary>
/// <param name="storageKind">Kind of the storage.</param>
/// <param name="storageNamespace">The storage namespace.</param>
/// <param name="tokenLeaseInSeconds">The token lease in seconds.</param>
public VssClientCredentialCachingStorage(string storageKind = "VssApp", string storageNamespace = "VisualStudio", double tokenLeaseInSeconds = DefaultTokenLeaseInSeconds)
: base(storageKind, storageNamespace)
{
this._tokenLeaseInSeconds = tokenLeaseInSeconds;
}
/// <summary>
/// Removes the token.
/// </summary>
/// <param name="serverUrl">The server URL.</param>
/// <param name="token">The token.</param>
public override void RemoveToken(Uri serverUrl, IssuedToken token)
{
this.RemoveToken(serverUrl, token, false);
}
/// <summary>
/// Removes the token.
/// </summary>
/// <param name="serverUrl">The server URL.</param>
/// <param name="token">The token.</param>
/// <param name="force">if set to <c>true</c> force the removal of the token.</param>
public void RemoveToken(Uri serverUrl, IssuedToken token, bool force)
{
//////////////////////////////////////////////////////////
// Bypassing this allows the token to be stored in local
// cache. Token is removed if lease is expired.
if (force || token != null && this.IsTokenExpired(token))
base.RemoveToken(serverUrl, token);
//////////////////////////////////////////////////////////
}
/// <summary>
/// Retrieves the token.
/// </summary>
/// <param name="serverUrl">The server URL.</param>
/// <param name="credentialsType">Type of the credentials.</param>
/// <returns>The <see cref="IssuedToken"/></returns>
public override IssuedToken RetrieveToken(Uri serverUrl, VssCredentialsType credentialsType)
{
var token = base.RetrieveToken(serverUrl, credentialsType);
if (token != null)
{
bool expireToken = this.IsTokenExpired(token);
if (expireToken)
{
base.RemoveToken(serverUrl, token);
token = null;
}
else
{
// if retrieving the token before it is expired,
// refresh the lease period.
this.RefreshLeaseAndStoreToken(serverUrl, token);
token = base.RetrieveToken(serverUrl, credentialsType);
}
}
return token;
}
/// <summary>
/// Stores the token.
/// </summary>
/// <param name="serverUrl">The server URL.</param>
/// <param name="token">The token.</param>
public override void StoreToken(Uri serverUrl, IssuedToken token)
{
this.RefreshLeaseAndStoreToken(serverUrl, token);
}
/// <summary>
/// Clears all tokens.
/// </summary>
/// <param name="url">The URL.</param>
public void ClearAllTokens(Uri url = null)
{
IEnumerable<VssToken> tokens = this.TokenStorage.RetrieveAll(base.TokenKind).ToList();
if (url != default(Uri))
tokens = tokens.Where(t => StringComparer.InvariantCultureIgnoreCase.Compare(t.Resource, url.ToString().TrimEnd('/')) == 0);
foreach(var token in tokens)
this.TokenStorage.Remove(token);
}
private void RefreshLeaseAndStoreToken(Uri serverUrl, IssuedToken token)
{
if (token.Properties == null)
token.Properties = new Dictionary<string, string>();
token.Properties[__tokenExpirationKey] = JsonSerializer.SerializeObject(this.GetNewExpirationDateTime());
base.StoreToken(serverUrl, token);
}
private DateTime GetNewExpirationDateTime()
{
var now = DateTime.Now;
// Ensure we don't overflow the max DateTime value
var lease = Math.Min((DateTime.MaxValue - now.Add(TimeSpan.FromSeconds(1))).TotalSeconds, this._tokenLeaseInSeconds);
// ensure we don't have negative leases
lease = Math.Max(lease, 0);
return now.AddSeconds(lease);
}
private bool IsTokenExpired(IssuedToken token)
{
bool expireToken = true;
if (token != null && token.Properties.ContainsKey(__tokenExpirationKey))
{
string expirationDateTimeJson = token.Properties[__tokenExpirationKey];
try
{
DateTime expiration = JsonSerializer.DeserializeObject<DateTime>(expirationDateTimeJson);
expireToken = DateTime.Compare(DateTime.Now, expiration) >= 0;
}
catch { }
}
return expireToken;
}
}
은 당신이 그들을 기대가 기억해야 할? 코드는 모든 호출에 대해'새로운 VssClientCredentials()'를 호출 할 것입니다. 'CreateConnection' 호출에 대한 값을 저장하는 것은 없습니다. – AdrianHHH
@AdrianHHH 문서에 따르면 VssClientCredentialStorage는 바로 그 기능을 수행합니다. – Jim
여기 예제를 참조하십시오 ... https : //docs.microsoft.com/en-us/vsts/integrate/concepts/dotnet-client-libraries – Jim