当我尝试将此JWT(由Azure移动服务发布)作为HTTP header /授权/承载 token 传递时:
Header:
{
"alg": "HS256",
"typ": "JWT",
"kid": "0"
}
Claims:
{
"ver": 2,
"aud": "Facebook",
"iss": "urn:microsoft:windows-azure:zumo",
"urn:microsoft:credentials": "pYK8b5...",
"exp": 1436730730,
"uid": "Facebook:10000xxxxxxxxxx"
}
进入配置的我的ASP.NET WEB API:
const string issuer = "urn:microsoft:windows-azure:zumo";
byte[] mobileServicesSecret = TextEncodings.Base64Url.Decode(ConfigurationManager.AppSettings["as:SecretKey"]);
app.UseJwtBearerAuthentication(
new JwtBearerAuthenticationOptions
{
AuthenticationMode = AuthenticationMode.Active,
AllowedAudiences = new[] { "Facebook" },
IssuerSecurityTokenProviders = new IIssuerSecurityTokenProvider[]
{
new SymmetricKeyIssuerSecurityTokenProvider(issuer, mobileServicesSecret)
}
});
我得到:
我怀疑这是因为存在“ child ”属性(property)吗?
编辑:使用此https://github.com/Magenic/JWTvalidator/tree/master/JwtValidator/JwtValidator,可以验证JWT,因此它没有错。但是我真的很想使用OWIN/Katana。
最佳答案
Google提出以下建议-
Calling the tokeninfo endpoint
CustomJwtHandler.cs
using System;
using System.Collections.Generic;
using System.IdentityModel.Tokens;
using System.Linq;
using System.Net.Http;
using System.Web;
using System.Web.Configuration;
using Newtonsoft.Json;
using System.Net;
using System.Threading.Tasks;
using System.Threading;
using Services.Models;
using System.Security.Claims;
namespace Services
{
/// <summary>
/// This is an implementation of Google JWT verification that
/// demonstrates:
/// - JWT validation
/// </summary>
/// @author [email protected] (Kunal Bajpai)
public class CustomJwtHandler : DelegatingHandler
{
private const string URL_GOOGLE_TOKEN_INFO = "https://www.googleapis.com/oauth2/v3/tokeninfo";
/// <summary>
///
/// </summary>
/// <param name="request"></param>
/// <param name="cancellationToken"></param>
/// <returns></returns>
protected override Task<HttpResponseMessage> SendAsync(HttpRequestMessage request, CancellationToken cancellationToken)
{
HttpStatusCode statusCode;
string token;
var authHeader = request.Headers.Authorization;
if (authHeader == null)
{
// Missing authorization header
return base.SendAsync(request, cancellationToken);
}
if (!TryRetrieveToken(request, out token))
{
return Task<HttpResponseMessage>.Factory.StartNew(() => new HttpResponseMessage(HttpStatusCode.Unauthorized));
}
try
{
ValidateToken(token);
return base.SendAsync(request, cancellationToken);
}
catch (SecurityTokenInvalidAudienceException)
{
statusCode = HttpStatusCode.Unauthorized;
}
catch (SecurityTokenValidationException)
{
statusCode = HttpStatusCode.Unauthorized;
}
catch (Exception)
{
statusCode = HttpStatusCode.InternalServerError;
}
return Task<HttpResponseMessage>.Factory.StartNew(() => new HttpResponseMessage(statusCode));
}
/// <summary>
/// Validates JWT Token
/// </summary>
/// <param name="JwtToken"></param>
private void ValidateToken(string JwtToken)
{
try
{
using (WebClient wc = new WebClient())
{
TokenInfo tokenInfo = JsonConvert.DeserializeObject<TokenInfo>(wc.DownloadString(URL_GOOGLE_TOKEN_INFO + "?id_token=" + JwtToken));
ClaimsPrincipal claimsPrincipal = new ClaimsPrincipal(new ClaimsIdentity(ExtractClaims(tokenInfo), tokenInfo.Issuer));
Thread.CurrentPrincipal = claimsPrincipal;
HttpContext.Current.User = claimsPrincipal;
}
}
catch (WebException e)
{
HttpStatusCode statusCode = ((HttpWebResponse)e.Response).StatusCode;
if (statusCode == HttpStatusCode.BadRequest)
{
throw new SecurityTokenValidationException();
}
else
{
throw new Exception();
}
}
}
/// <summary>
/// Tries to retrieve Token
/// </summary>
/// <param name="request"></param>
/// <param name="token"></param>
/// <returns></returns>
private static bool TryRetrieveToken(HttpRequestMessage request, out string token)
{
token = null;
IEnumerable<string> authorizationHeaders;
if (!request.Headers.TryGetValues("Authorization", out authorizationHeaders) ||
authorizationHeaders.Count() > 1)
{
return false;
}
var bearerToken = authorizationHeaders.ElementAt(0);
token = bearerToken.StartsWith("Bearer ") ? bearerToken.Substring(7) : bearerToken;
return true;
}
private List<Claim> ExtractClaims(TokenInfo tokenInfo)
{
List<Claim> claims = new List<Claim> {
new Claim(ClaimTypes.Name, tokenInfo.Name),
new Claim(ClaimTypes.Email, tokenInfo.Email),
new Claim(ClaimTypes.GivenName, tokenInfo.GivenName),
new Claim(ClaimTypes.Surname, tokenInfo.FamilyName),
new Claim(ApplicationUser.CLAIM_TYPE_LOCALE, tokenInfo.Locale),
new Claim(ClaimTypes.NameIdentifier, tokenInfo.ProviderKey, ClaimValueTypes.String, tokenInfo.Issuer),
new Claim(ApplicationUser.CLAIM_TYPE_EMAIL_CONFIRMED, tokenInfo.IsEmailVerifed.ToString(), ClaimValueTypes.Boolean)
};
return claims;
}
}
}
TokenInfo.cs using Microsoft.AspNet.Identity.EntityFramework;
using Newtonsoft.Json;
namespace Services.Models
{
public class TokenInfo
{
[JsonProperty("iss")]
public string Issuer { get; set; }
[JsonProperty("aud")]
public string AudienceClientId { get; set; }
[JsonProperty("sub")]
public string ProviderKey { get; set; }
[JsonProperty("email_verified")]
public bool IsEmailVerifed { get; set; }
[JsonProperty("azp")]
public string AndroidClientId { get; set; }
[JsonProperty("email")]
public string Email { get; set; }
[JsonProperty("iat")]
public long IssuedAt { get; set; }
[JsonProperty("exp")]
public long ExpiresAt { get; set; }
[JsonProperty("name")]
public string Name { get; set; }
[JsonProperty("picture")]
public string Picture { get; set; }
[JsonProperty("given_name")]
public string GivenName { get; set; }
[JsonProperty("family_name")]
public string FamilyName { get; set; }
[JsonProperty("locale")]
public string Locale { get; set; }
[JsonProperty("alg")]
public string Algorithm { get; set; }
[JsonProperty("kid")]
public string kid { get; set; }
public override bool Equals(object obj)
{
if (obj.GetType() != typeof(ApplicationUser))
{
return false;
}
ApplicationUser user = (ApplicationUser)obj;
bool hasLogin = false;
foreach (IdentityUserLogin login in user.Logins)
{
if (login.ProviderKey == ProviderKey)
{
hasLogin = true;
break;
}
}
if (!hasLogin) { return false; }
if (user.FirstName != GivenName) { return false; }
if (user.LastName != FamilyName) { return false; }
if (user.Locale != Locale) { return false; }
return base.Equals(obj);
}
}
}
WebApiConfig.cs using System;
using System.Collections.Generic;
using System.Linq;
using System.Net.Http;
using System.Web.Http;
using Microsoft.Owin.Security.OAuth;
using Newtonsoft.Json.Serialization;
namespace Services
{
public static class WebApiConfig
{
public static void Register(HttpConfiguration config)
{
// Web API configuration and services
// Configure Web API to use only bearer token authentication.
config.SuppressDefaultHostAuthentication();
config.Filters.Add(new HostAuthenticationFilter(OAuthDefaults.AuthenticationType));
// Web API routes
config.MapHttpAttributeRoutes();
config.Routes.MapHttpRoute(
name: "DefaultApi",
routeTemplate: "api/{controller}/{id}",
defaults: new { id = RouteParameter.Optional }
);
config.MessageHandlers.Add(new CustomJwtHandler());
}
}
}
关于c# - 为什么会出现SecurityTokenSignatureKeyNotFoundException?,我们在Stack Overflow上找到一个类似的问题:https://stackoverflow.com/questions/30821510/