哇,我做到了。我想到了。我......我無法相信。
正如我在問題更新2中提到的,此代碼是從谷歌的官方API C#示例和Microsoft的Custom AuthenticationFilter教程和代碼示例彙編而成的。我要在這裏粘貼AuthorizeAsync(),並查看每個代碼塊的作用。如果您認爲您遇到問題,請隨時提及。
public async Task AuthenticateAsync(HttpAuthenticationContext context, CancellationToken cancellationToken)
{
bool token_valid = false;
HttpRequestMessage request = context.Request;
// 1. Look for credentials in the request
//Trace.TraceInformation(request.ToString());
string idToken = request.Headers.Authorization.Parameter.ToString();
客戶端將授權標頭字段添加到方案後跟一個空格,後跟id標記。它看起來像Authorization: id-token-goog IaMS0m3.Tok3nteXt...
。將ID標記放在Google文檔中給出的正文中對此過濾器沒有任何意義,因此我決定將它放在標題中。出於某種原因,很難從HTTP數據包中提取自定義標題,所以我只是決定使用帶有ID標記的自定義方案的Authorization標頭。
// 2. If there are no credentials, do nothing.
if (idToken == null)
{
Trace.TraceInformation("No credentials.");
return;
}
// 3. If there are credentials, but the filter does not recognize
// the authentication scheme, do nothing.
if (request.Headers.Authorization.Scheme != "id-token-goog")
// Replace this with a more succinct Scheme title.
{
Trace.TraceInformation("Bad scheme.");
return;
}
這整個過濾器的一點是忽略過濾器不治理(AUTH陌生計劃等),並就它的應該管理的要求判決的請求。允許有效的身份驗證傳遞給下游的AuthorizeFilter或直接傳遞給Controller。
我編制了「id-token-goog」這個方案,因爲我不知道這個用例是否有現有的方案。如果有的話,請讓我知道,我會解決它。我想現在並不特別重要,只要我的客戶都知道這個計劃。
// 4. If there are credentials that the filter understands, try to validate them.
if (idToken != null)
{
JwtSecurityToken token = new JwtSecurityToken(idToken);
JwtSecurityTokenHandler jsth = new JwtSecurityTokenHandler();
// Configure validation
Byte[][] certBytes = getCertBytes();
Dictionary<String, X509Certificate2> certificates =
new Dictionary<String, X509Certificate2>();
for (int i = 0; i < certBytes.Length; i++)
{
X509Certificate2 certificate =
new X509Certificate2(certBytes[i]);
certificates.Add(certificate.Thumbprint, certificate);
}
{
// Set up token validation
TokenValidationParameters tvp = new TokenValidationParameters()
{
ValidateActor = false, // check the profile ID
ValidateAudience =
(CLIENT_ID != ConfigurationManager
.AppSettings["GoogClientID"]), // check the client ID
ValidAudience = CLIENT_ID,
ValidateIssuer = true, // check token came from Google
ValidIssuer = "accounts.google.com",
ValidateIssuerSigningKey = true,
RequireSignedTokens = true,
CertificateValidator = X509CertificateValidator.None,
IssuerSigningKeyResolver = (s, securityToken, identifier, parameters) =>
{
return identifier.Select(x =>
{
// TODO: Consider returning null here if you have case sensitive JWTs.
/*if (!certificates.ContainsKey(x.Id))
{
return new X509SecurityKey(certificates[x.Id]);
}*/
if (certificates.ContainsKey(x.Id.ToUpper()))
{
return new X509SecurityKey(certificates[x.Id.ToUpper()]);
}
return null;
}).First(x => x != null);
},
ValidateLifetime = true,
RequireExpirationTime = true,
ClockSkew = TimeSpan.FromHours(13)
};
這和谷歌的例子沒有任何變化。我幾乎不知道它做了什麼。這基本上在創建JWTSecurityToken(標記字符串的解析,解碼版本)中設置了一些魔力,並設置了驗證參數。我不確定爲什麼本節的底部部分在它自己的語句塊中,但它與CLIENT_ID和該比較有關。我不知道何時或爲什麼CLIENT_ID的值將永遠改變,但顯然有必要...
try
{
// Validate using the provider
SecurityToken validatedToken;
ClaimsPrincipal cp = jsth.ValidateToken(idToken, tvp, out validatedToken);
if (cp != null)
{
cancellationToken.ThrowIfCancellationRequested();
ApplicationUserManager um =
context
.Request
.GetOwinContext()
.GetUserManager<ApplicationUserManager>();
從OWIN背景下獲取用戶的經理。我不得不在context
intellisense中搜索,直到找到GetOwinCOntext()
,然後發現我必須添加using Microsoft.Aspnet.Identity.Owin;
才能添加包含方法GetUserManager<>()
的部分類。
ApplicationUser au =
await um
.FindAsync(
new UserLoginInfo(
"Google",
token.Subject)
);
這是我必須解決的最後一件事。再次,我不得不通過um
Intellisense來查找所有Find函數及其覆蓋。我從我的數據庫中的Identity Framework創建的表中注意到,有一個名爲UserLogin的行,其行包含提供者,提供者密鑰和用戶FK。 FindAsync()
需要一個UserLoginInfo
對象,其中只包含提供程序字符串和提供程序密鑰。我有一種預感,現在這兩件事情是相關的。我還記得,有一個令牌格式的字段,其中包括一個以1開頭的長鍵字段。
validatedToken
似乎基本上是空的,不是null,而是一個空的SecurityToken。這就是爲什麼我使用token
而不是validatedToken
。我在想這肯定有什麼問題,但是由於cp
不是null,這是驗證失敗的有效檢查,所以原始標記有效就足夠了。
// If there is no user with those credentials, return
if (au == null)
{
return;
}
ClaimsIdentity identity =
await um
.ClaimsIdentityFactory
.CreateAsync(um, au, "Google");
context.Principal = new ClaimsPrincipal(identity);
token_valid = true;
在這裏,我要創建一個新的ClaimsPrincipal因爲在驗證上面創建的一個是空的(顯然這是正確的)。猜測CreateAsync()
的第三個參數應該是什麼。它似乎是這樣工作的。
}
}
catch (Exception e)
{
// Multiple certificates are tested.
if (token_valid != true)
{
Trace.TraceInformation("Invalid ID Token.");
context.ErrorResult =
new AuthenticationFailureResult(
"Invalid ID Token.", request);
}
if (e.Message.IndexOf("The token is expired") > 0)
{
// TODO: Check current time in the exception for clock skew.
Trace.TraceInformation("The token is expired.");
context.ErrorResult =
new AuthenticationFailureResult(
"Token is expired.", request);
}
Trace.TraceError("Error occurred: " + e.ToString());
}
}
}
}
其餘的只是異常捕捉。
感謝您檢查了這一點。希望您可以查看我的源代碼,並查看哪些組件來自哪個代碼庫。
對於Android客戶端谷歌表示,請按照「跨客戶端流量」https://developers.google.com/identity/protocols/CrossClientAuth#accessTokens。使用GoogleAuthUtil.getToken()獲取JWT令牌的位置。這不工作,並使用標準的OWIN流程來驗證谷歌令牌 – Haroon
@LavaHot - 你能提供完整的代碼嗎? –
謝謝!!!!!你幫了我很多!幫助我理解這個完整的解決方案:http://stackoverflow.com/questions/27754171/mvc-5-web-api-with-facebook-access-token-to-registerexternal-without-need-of-coo#answer- 28298790 –