JWKS是JSON Web Keys的缩写。如果API Service使用第三方的JWT Token来做认证和授权,那么通常第三方会提供一个JWKS地址,这个地址里有用来验证token的公钥。
下面是从 okta 取到的一个 JWKS 的内容示例。我们可以看到这里有两个RSA RS256 公钥。
{
"keys": [
{
"kty": "RSA",
"alg": "RS256",
"kid": "hE2MW0jokw9QCkomGHA9zNAxnxIUc_vCVGmdc0DyE1U",
"use": "sig",
"e": "AQAB",
"n": "p4s6EnKfkDU8fVh6QWOcWEDvBzFQO6EKx0URotAjj-3yakrRvhS7O9CYx6QUY8URjHfc3vgW5Do86Fgf0AcJVk_UnKhn_K4_MIzvSBspX8lIHKa7BZIstW9CcfRVtCQ1o8uhcJdRBPZ9SvqJZsK-zkIukARiqezH90tFKaDN1mYx3b9t0hVF6d6RtSZUwRphqpr7c9A_ySVT44QXYzHfuojypxlUSqx5pu4bwEedIlsyYNArzB3wdBfUXYM-8W5kiB5ntVWCPLLFrv4DilHGpMsAcrLn1iEFC0F-4skU-kkyhe3uw8cnjGXCbaOZnC6dJ1BsUkwYsS53o5rRsTt-xw"
},
{
"kty": "RSA",
"alg": "RS256",
"kid": "Z6RuPqgZz9RVgZWEvVXvbUtu9o4sFmkE03DpcXrnJZ8",
"use": "sig",
"e": "AQAB",
"n": "i223012Y_-gKCki2CkH4Xt3nk7P74Dliq6TolKWRs13nqTL69VMR_u3ewJ0Fuhe7odF_mxHs-kKe88AG_tQgSpxfsWoa0Cqa6cdudbe3Xq55Vgj2ghyF2pRAZCNU983--Kbfqxi2WyMeNnL8FD2us6euB-TWVfE4OL20p0PJbg46uMhjLgefXrOYY1sqrvGr_D2Jb4hHcaA0CY7RF8bYh4YV3ExJW2zH8bFBN0wnQnsCDtdLtL-ud_X_52ttQ2a2_VnUDabw6imEZ-ydmjjwtRPDFO8gvoGsyTiN7pgvbo4M4iePK02_Z_xLmwnsqiXP59NP_BTjOhksYBqUNO_YLQ"
}
]
}
ASP.NET CORE 是通过支持 OpenId 协议来支持 JWKS 的,如果开发者只使用到 JWKS,那么开发者需要自己修改 OpenId 的参数来支持 JWKS 或者写单独的代码通过手动下载公钥和设置 TokenValidationParameters.IssuerSigningKeys 来解决问题。
幸运的是强大的开源社区给我们提供了解决方案,使用 Nuget 安装 NetDevPack.JwtExtensions,然后在 StartUp.ConfigureServices 里使用类似于下面的代码就可以简单的使用 JWKS 了。
// JWKS
var jwksUrl = this.Configuration.GetValue<string>("Authentication:Jwks_uri");
var issuer = this.Configuration.GetValue<string>("Authentication:ValidIssuer");
var validateIssuer = this.Configuration.GetValue<bool>("Authentication:ValidateIssuer");
services.AddAuthentication(o =>
{
o.DefaultAuthenticateScheme = JwtBearerDefaults.AuthenticationScheme;
o.DefaultChallengeScheme = JwtBearerDefaults.AuthenticationScheme;
})
.AddJwtBearer(options =>
{
// 加上下面的cert选项,你自己签名的证书也可以通过了
var httpClientHandler = new HttpClientHandler();
httpClientHandler.ServerCertificateCustomValidationCallback = HttpClientHandler.DangerousAcceptAnyServerCertificateValidator;
options.SetJwksOptions(new JwkOptions(jwksUrl));
options.RequireHttpsMetadata = false;
options.SaveToken = true;
options.TokenValidationParameters = new Microsoft.IdentityModel.Tokens.TokenValidationParameters
{
ValidateIssuer = validateIssuer,
ValidIssuer = issuer,
ValidateAudience = false,
ValidateLifetime = true,
RequireExpirationTime = true
};
});
注意Github上只说使用 options.SetJwksOptions(new JwkOptions(jwksUrl)) 就可以了,实际上是不行的,如果不设置 TokenValidationParameters 相关的参数,你可能会看到类似下面的错误。切记!另外,证书是自己签名的,就需要加上 HttpClientHandler.DangerousAcceptAnyServerCertificateValidator 这句话,要不会看到 ssl 连接错误。
最后别忘了在 Startup.Configure 里加上
public void Configure(IApplicationBuilder app, IWebHostEnvironment env, IApiVersionDescriptionProvider provider)
{
...
app.UseAuthentication();
app.UseAuthorization();
...
}