我正在尝试使用oAuth 2.0中间件验证JWT。我尝试在我的Startup.cs类中使用自定义Provider:
public class Startup
{
public void Configuration(IAppBuilder app)
{
HttpConfiguration config = new HttpConfiguration();
// Web API routes
config.MapHttpAttributeRoutes();
ConfigureOAuth(app);
app.UseCors(Microsoft.Owin.Cors.CorsOptions.AllowAll);
app.UseWebApi(config);
}
public void ConfigureOAuth(IAppBuilder app)
{
OAuthAuthorizationServerOptions OAuthServerOptions = new OAuthAuthorizationServerOptions()
{
//For Dev enviroment only (on production should be AllowInsecureHttp = false)
AllowInsecureHttp = true,
TokenEndpointPath = new PathString("/oauth2/token"),
AccessTokenExpireTimeSpan = TimeSpan.FromMinutes(5),
Provider = new CustomOAuthProvider(),
AccessTokenFormat = new RMAJwtAuthenticator.CustomJwtFormat("www.abc.com")
};
// OAuth 2.0 Bearer Access Token Generation
app.UseOAuthAuthorizationServer(OAuthServerOptions);
// start : Code for Validating JWT
var issuer = "www.abc.com";
var audience = "www.xyz.com";
var secret = TextEncodings.Base64Url.Decode("Yuer534553HDS&dsa");
// Api controllers with an [Authorize] attribute will be validated with JWT
app.UseJwtBearerAuthentication(
new JwtBearerAuthenticationOptions
{
AuthenticationMode = AuthenticationMode.Active,
AllowedAudiences = new[] { audience },
IssuerSecurityTokenProviders = new IIssuerSecurityTokenProvider[]
{
new SymmetricKeyIssuerSecurityTokenProvider(issuer, secret)
},
Provider = new CustomOAuthBearerProvider()
});
//End: Code for Validating JWT
}
}
在继承IOAuthBearerAuthenticationProvider的CustomOAuthBearerProvider中,我提供了ApplyChallenge(),RequestToken()和ValidateIdentity()的定义:
public class CustomOAuthBearerProvider : IOAuthBearerAuthenticationProvider
{
public Task ApplyChallenge(OAuthChallengeContext context)
{
return Task.FromResult<object>(null);
}
public Task RequestToken(OAuthRequestTokenContext context)
{
return Task.FromResult<object>(null);
}
public Task ValidateIdentity(OAuthValidateIdentityContext context)
{
return Task.FromResult<object>(null);
}
}
现在,当我试图获取授权资源时,第一个RequestToken()被点击,然后我不知道如何验证JWT并将控制传递给ValidateIdentity()方法。
我想自定义验证过程的原因是为了保存和延长我的JWT在数据库中的过期时间(你也可以建议任何事情来增加JWT的过期时间而不改变原始令牌)。
请评论,无论您有什么想法/建议/好的不良练习选项/链接都会有所帮助。 谢谢。
答案 0 :(得分:3)
实际上我们可以对JWT进行自定义验证。我创建了一个没有过期时间的JWT并通过其签名验证了它,当我们在Jwt中保持到期时间时也可以这样做。 现在和之前一样,而不是使用JWTBearerAuthentication,我们可以像下面这样使用OAuthBearerAuthentication :
public void ConfigureOAuth(IAppBuilder app)
{
OAuthAuthorizationServerOptions OAuthServerOptions = new OAuthAuthorizationServerOptions()
{
//For Dev enviroment only (on production should be AllowInsecureHttp = false)
AllowInsecureHttp = true,
TokenEndpointPath = new PathString("/api/token"),
//provide Expire Time
//AccessTokenExpireTimeSpan = TimeSpan.FromMinutes(5),
Provider = new CustomOAuthProvider(),
//provide issuer name/url
//
AccessTokenFormat = new RMAJwtAuthenticator.CustomJwtFormat("www.abc.com")
};
// OAuth 2.0 Bearer Access Token Generation
app.UseOAuthAuthorizationServer(OAuthServerOptions);
//// start : Code for Validating JWT
OAuthBearerAuthenticationOptions OAuthBearerOptions = new OAuthBearerAuthenticationOptions()
{
AccessTokenFormat = OAuthServerOptions.AccessTokenFormat,
AccessTokenProvider = OAuthServerOptions.AccessTokenProvider,
AuthenticationMode = OAuthServerOptions.AuthenticationMode,
AuthenticationType = OAuthServerOptions.AuthenticationType,
Description = OAuthServerOptions.Description,
Provider = new CustomOAuthBearerProvider()
};
app.UseOAuthBearerAuthentication(OAuthBearerOptions);
//////End: Code for Validating JWT
}
你可以使用相同的 CustomJwtFormat 类来创建你的JWT,通过在ISecureDataFormat接口中声明的 UnProtect 方法来验证你的JWT:
public class CustomJwtFormat : ISecureDataFormat<AuthenticationTicket>
{
//Needs to be configured in Configuration file
const string AudiencePropertyKey = "audience";
const string signatureAlgorithm = "www.w3.org/2001/04/xmldsig-more#hmac-sha256";
const string digestAlgorithm = "www.w3.org/2001/04/xmlenc#sha256";
private readonly string _issuer = string.Empty;
public CustomJwtFormat(string issuer)
{
_issuer = issuer;
}
/// <summary>
/// Creates JWT Token here, using AuthenticationTicket
/// </summary>
/// <param name="data"></param>
/// <returns></returns>
public string Protect(AuthenticationTicket data)
{
JwtAuthHelper objJwtAuthHelper = new JwtAuthHelper();
try
{
if (data == null)
{
throw new ArgumentNullException("data");
}
string audienceId = data.Properties.Dictionary.ContainsKey(AudiencePropertyKey) ? data.Properties.Dictionary[AudiencePropertyKey] : null;
if (string.IsNullOrWhiteSpace(audienceId)) throw new InvalidOperationException("AuthenticationTicket.Properties does not include audience");
//check if audience is valid (in case of audience is stored in DB or some list)
Audience audience = AudiencesStore.FindAudience(audienceId);
//In case , if each audience has separate secretKey
//Right now we have a common secret key
if (audience != null)
{
var symmetricKey = TextEncodings.Base64Url.Decode(audience.EncryptedSecret);//any encrypted (or simple) key from 3rd party client
//***added refernce of System.IdenityModel to get SigningCredentials class refernce
// instead of using ThinkTecture nugget packaged dlls
var SigningCredentials = new SigningCredentials(new InMemorySymmetricSecurityKey(symmetricKey), signatureAlgorithm, digestAlgorithm);
var issued = data.Properties.IssuedUtc;
var expires = data.Properties.ExpiresUtc;
//Modified to keep issued and expirey time as NULL
var token = new JwtSecurityToken(_issuer, audienceId, data.Identity.Claims, null, null, SigningCredentials);
//var token = new JwtSecurityToken(_issuer, audienceId, data.Identity.Claims, issued.Value.UtcDateTime, expires.Value.UtcDateTime, SigningCredentials);
var handler = new JwtSecurityTokenHandler();
var jwt = handler.WriteToken(token);
}
return string.Empty;
}
catch (Exception)
{
throw;
}
}
/// <summary>
/// UnProtect ticket : Validates JWT
/// </summary>
/// <param name="protectedText"></param>
/// <returns></returns>
public AuthenticationTicket Unprotect(string protectedText)
{
// start : Code for Validating JWT
//JwtSecurityTokenHandler
System.IdentityModel.Tokens.JwtSecurityTokenHandler tokenHandler = new System.IdentityModel.Tokens.JwtSecurityTokenHandler();
System.Security.Claims.ClaimsPrincipal claimsPrincipal;
try
{
System.IdentityModel.Tokens.JwtSecurityToken tokenReceived = new System.IdentityModel.Tokens.JwtSecurityToken(protectedText);
//Configure Validation parameters// Now its Generalized//token must have issuer and audience
var issuer = tokenReceived.Issuer;
List<string> strAudience = (List<string>)tokenReceived.Audiences;
var audience = strAudience.Count > 0 ? strAudience[0].ToString(): string.Empty;
Audience audForContext = AudiencesStore.FindAudience(audience);
var symmetricKey = Microsoft.Owin.Security.DataHandler.Encoder.TextEncodings.Base64Url.Decode(audForContext.EncryptedSecret);
var validationParameters = new System.IdentityModel.Tokens.TokenValidationParameters()
{
ValidAudience = audience,
IssuerSigningKey = new System.IdentityModel.Tokens.InMemorySymmetricSecurityKey(symmetricKey),
ValidIssuer = issuer,
RequireExpirationTime = false
};
System.IdentityModel.Tokens.SecurityToken validatedToken;
//if token gets validated claimsPrincipal has value otherwise it throws exception
claimsPrincipal = tokenHandler.ValidateToken(protectedText, validationParameters, out validatedToken);
var props = new AuthenticationProperties(new Dictionary<string, string> { { "audience", audience } });
var ticket = new AuthenticationTicket((System.Security.Claims.ClaimsIdentity)claimsPrincipal.Identity, props);
return ticket;
}
catch (Exception)
{
throw;
}
////End: Custom code to handle Validate Token
}
}