API 在 Azure 上返回 401 但在 运行 本地时不返回
API returning 401 on Azure but not when run locally
有问题的应用程序是一个网站,它有一个 API 附加在一侧,可以重用为该网站开发的许多数据访问方法。因此,网站 authentication/authorization 和 API 之间可能存在一些干扰。但如果是这样的话,我不明白为什么它在本地有效。
当我在本地 运行 时,我可以测试 API 使用 Swagger 或 Postman 登录,获取 Bearer 令牌并使用它来调用 API 方法。在 Azure 上,虽然登录成功,但下一次调用 API returns a 401:
www-authenticate: Bearer error="invalid_token",error_description="The signature is invalid"
最明显的区别是 appsettings.json,我已将其复制到 Azure 上的应用程序配置 blade:
原始应用程序设置如下所示:
"Rt5": {
"BaseAddress": "https://localhost:44357"
},
"Azure": {
"BuildNumber": ""
},
"AuthentificationJWT": {
"Audience": "ddt-ssp",
"Issuer": "ddt-rt5",
"SecurityKey": "not so secret"
},
用法 - ConfigureServices():
services.AddAuthentication(CookieAuthenticationDefaults.AuthenticationScheme)
.AddCookie(CookieAuthenticationDefaults.AuthenticationScheme, options =>
{
options.AccessDeniedPath = "/Home/Unauthorized";
options.LoginPath = "/User/Login";
options.LogoutPath = "/User/Logout";
})
.AddJwtBearer(JwtBearerDefaults.AuthenticationScheme, options =>
{
options.TokenValidationParameters = new TokenValidationParameters
{
ValidateIssuerSigningKey = true,
IssuerSigningKey = new SymmetricSecurityKey(Encoding.UTF8.GetBytes(Configuration["AuthentificationJWT:SecurityKey"])),
ValidateIssuer = true,
ValidIssuer = Configuration["AuthentificationJWT:Issuer"],
ValidateAudience = true,
ValidAudience = Configuration["AuthentificationJWT:Audience"],
ValidateLifetime = true,
ValidAlgorithms = new HashSet<string> { SecurityAlgorithms.HmacSha256 },
ValidTypes = new HashSet<string> { "JWT" }
};
});
services.AddAuthorization(o =>
{
o.AddPolicy(AllowViewExceptions.Policy, p
=> p.Requirements.Add(new AllowViewExceptions.Requirement()));
o.AddPolicy(AllowSubmitException.Policy, p
=> p.Requirements.Add(new AllowSubmitException.Requirement()));
});
用法-配置():
app.UseHttpsRedirection();
app.UseStaticFiles();
app.UseRouting();
app.UseCookiePolicy();
app.UseAuthentication();
app.UseAuthorization();
if (UseSwagger)
{
app.UseSwagger();
app.UseSwaggerUI(c => { c.SwaggerEndpoint("/swagger/v1/swagger.json", "API v1"); });
}
app.UseEndpoints(endpoints =>
{
endpoints.MapControllerRoute(
name: "default",
pattern: "{controller=Home}/{action=Index}/{id?}");
});
坦白:这不是我的代码,我确定为什么我们提到了 Cookie 内容,但似乎确实提供了另一种授权机制。看这里:
public string GetAuthorization()
{
//TODO: use ExtractSecurityToken();
var claimToken = _httpContextAccessor.HttpContext.User.Claims.FirstOrDefault(c => c.Type == AuthenticationConfig.AccessTokenCookieName);
if (claimToken != null)
{
return $"Bearer {claimToken.Value}";
}
else if (_httpContextAccessor.HttpContext.Request.Headers.TryGetValue(AuthenticationConfig.AuthorizationHeader, out var headerToken))
{
return headerToken;
}
return null;
}
private JwtSecurityToken ExtractSecurityToken()
{
var accessToken = _httpContextAccessor.HttpContext.User.Claims.FirstOrDefault(c => c.Type == AuthenticationConfig.AccessTokenCookieName)?.Value;
if (accessToken == null &&
_httpContextAccessor.HttpContext.Request.Headers.TryGetValue(AuthenticationConfig.AuthorizationHeader, out var value))
{
accessToken = value.ToString().Replace("Bearer ", ""); // TODO: Find a better way then extracting Bearer e.g Get token without scheme
}
if (accessToken == null)
{
return null;
}
return (JwtSecurityToken)new JwtSecurityTokenHandler().ReadToken(accessToken);
}
问题似乎与 Azure 网站的“身份验证/授权”选项有关,启用后会阻止 Web Api 接受身份验证 header 的请求。禁用该选项并将 Owin 库与 Azure AD 一起使用可提供所需的解决方案。
每个与 Azure-AD 关联的应用程序服务都有一个相应的 Azure-AD Web app/API 类型的应用程序声明。此资源 ID 是应用服务 Azure-AD 应用程序声明中的“App ID URI”。
要调试身份验证问题,请参考此msdn link。
原来问题是由应用程序周围的奇怪架构引起的。访问令牌值(包括使用安全密钥计算的 SHA256 校验和)正在另一个应用程序中生成并传递给此应用程序以用于其通信。
当 运行 本地化时,两个应用程序都使用存储在其 appsettings.json 文件中的安全密钥并且是相同的。将这些应用程序部署到 Azure 的管道正在用安全密钥替换随机值,因此它们不同。
我没有删除这里的问题,因为 Suryasri 的 link 以后可能会对其他人有所帮助。
有问题的应用程序是一个网站,它有一个 API 附加在一侧,可以重用为该网站开发的许多数据访问方法。因此,网站 authentication/authorization 和 API 之间可能存在一些干扰。但如果是这样的话,我不明白为什么它在本地有效。
当我在本地 运行 时,我可以测试 API 使用 Swagger 或 Postman 登录,获取 Bearer 令牌并使用它来调用 API 方法。在 Azure 上,虽然登录成功,但下一次调用 API returns a 401:
www-authenticate: Bearer error="invalid_token",error_description="The signature is invalid"
最明显的区别是 appsettings.json,我已将其复制到 Azure 上的应用程序配置 blade:
原始应用程序设置如下所示:
"Rt5": {
"BaseAddress": "https://localhost:44357"
},
"Azure": {
"BuildNumber": ""
},
"AuthentificationJWT": {
"Audience": "ddt-ssp",
"Issuer": "ddt-rt5",
"SecurityKey": "not so secret"
},
用法 - ConfigureServices():
services.AddAuthentication(CookieAuthenticationDefaults.AuthenticationScheme)
.AddCookie(CookieAuthenticationDefaults.AuthenticationScheme, options =>
{
options.AccessDeniedPath = "/Home/Unauthorized";
options.LoginPath = "/User/Login";
options.LogoutPath = "/User/Logout";
})
.AddJwtBearer(JwtBearerDefaults.AuthenticationScheme, options =>
{
options.TokenValidationParameters = new TokenValidationParameters
{
ValidateIssuerSigningKey = true,
IssuerSigningKey = new SymmetricSecurityKey(Encoding.UTF8.GetBytes(Configuration["AuthentificationJWT:SecurityKey"])),
ValidateIssuer = true,
ValidIssuer = Configuration["AuthentificationJWT:Issuer"],
ValidateAudience = true,
ValidAudience = Configuration["AuthentificationJWT:Audience"],
ValidateLifetime = true,
ValidAlgorithms = new HashSet<string> { SecurityAlgorithms.HmacSha256 },
ValidTypes = new HashSet<string> { "JWT" }
};
});
services.AddAuthorization(o =>
{
o.AddPolicy(AllowViewExceptions.Policy, p
=> p.Requirements.Add(new AllowViewExceptions.Requirement()));
o.AddPolicy(AllowSubmitException.Policy, p
=> p.Requirements.Add(new AllowSubmitException.Requirement()));
});
用法-配置():
app.UseHttpsRedirection();
app.UseStaticFiles();
app.UseRouting();
app.UseCookiePolicy();
app.UseAuthentication();
app.UseAuthorization();
if (UseSwagger)
{
app.UseSwagger();
app.UseSwaggerUI(c => { c.SwaggerEndpoint("/swagger/v1/swagger.json", "API v1"); });
}
app.UseEndpoints(endpoints =>
{
endpoints.MapControllerRoute(
name: "default",
pattern: "{controller=Home}/{action=Index}/{id?}");
});
坦白:这不是我的代码,我确定为什么我们提到了 Cookie 内容,但似乎确实提供了另一种授权机制。看这里:
public string GetAuthorization()
{
//TODO: use ExtractSecurityToken();
var claimToken = _httpContextAccessor.HttpContext.User.Claims.FirstOrDefault(c => c.Type == AuthenticationConfig.AccessTokenCookieName);
if (claimToken != null)
{
return $"Bearer {claimToken.Value}";
}
else if (_httpContextAccessor.HttpContext.Request.Headers.TryGetValue(AuthenticationConfig.AuthorizationHeader, out var headerToken))
{
return headerToken;
}
return null;
}
private JwtSecurityToken ExtractSecurityToken()
{
var accessToken = _httpContextAccessor.HttpContext.User.Claims.FirstOrDefault(c => c.Type == AuthenticationConfig.AccessTokenCookieName)?.Value;
if (accessToken == null &&
_httpContextAccessor.HttpContext.Request.Headers.TryGetValue(AuthenticationConfig.AuthorizationHeader, out var value))
{
accessToken = value.ToString().Replace("Bearer ", ""); // TODO: Find a better way then extracting Bearer e.g Get token without scheme
}
if (accessToken == null)
{
return null;
}
return (JwtSecurityToken)new JwtSecurityTokenHandler().ReadToken(accessToken);
}
问题似乎与 Azure 网站的“身份验证/授权”选项有关,启用后会阻止 Web Api 接受身份验证 header 的请求。禁用该选项并将 Owin 库与 Azure AD 一起使用可提供所需的解决方案。
每个与 Azure-AD 关联的应用程序服务都有一个相应的 Azure-AD Web app/API 类型的应用程序声明。此资源 ID 是应用服务 Azure-AD 应用程序声明中的“App ID URI”。
要调试身份验证问题,请参考此msdn link。
原来问题是由应用程序周围的奇怪架构引起的。访问令牌值(包括使用安全密钥计算的 SHA256 校验和)正在另一个应用程序中生成并传递给此应用程序以用于其通信。
当 运行 本地化时,两个应用程序都使用存储在其 appsettings.json 文件中的安全密钥并且是相同的。将这些应用程序部署到 Azure 的管道正在用安全密钥替换随机值,因此它们不同。
我没有删除这里的问题,因为 Suryasri 的 link 以后可能会对其他人有所帮助。