我有一组 API 调用,它们通过 Azure APIM 到达我们的后端。鉴于包含订阅密钥,除其中一个调用外,所有调用均按其应有的方式运行。然而,尽管在标题部分分配了订阅密钥,但下面的函数始终会产生
401: access denied, missing subscription key
。为了保持帖子整洁,我将排除额外的类型,因为它们很快就会使帖子变得混乱,而且我不认为它们是原因。
export const webApiConfig: WebApiConfig = {
endpointUrl: process.env.REACT_APP_WEBAPI_ENDPOINT as string,
subscriptionKey: process.env.REACT_APP_WEBAPI_GATEWAY_SUBSCRIPTION_KEY as string,
};
export const trainProjectAsync = async (projectName: string): Promise<NluTrainingState> => {
try {
// Build the request url and config
const url: string = `${webApiConfig.endpointUrl}/Nlu/${projectName}/Train`;
const token = await getToken();
const requestConfig: AxiosRequestConfig = {
headers: {
Authorization: token,
"Content-Type": "application/json",
"Ocp-Apim-Subscription-Key": webApiConfig.subscriptionKey,
},
signal: new AbortController().signal,
};
// Perform the POST request
const response = await axios.post<NluTrainingState>(url, requestConfig);
return response.data satisfies NluTrainingState;
} catch (error) {
// check whether the error is an axios error or a stock error.
if (axios.isAxiosError(error)) {
throw new Error(`API call axios malfunction code: ${error.code} ${JSON.stringify(error.response)}`);
} else {
throw new Error(`API call stock malfunction, reason: ${error}`);
}
}
};
这是应该通过 APIM 调用的函数(通过 postman 或 swagger 测试它表明该函数可以正常工作)。但是,当 API 本地托管且中间没有 API 网关时,前端 API 调用也会产生
401
结果。
[Authorize]
[HttpPost("{projectName}/Train")]
[Produces("application/json")]
[ProducesResponseType(StatusCodes.Status200OK)]
[ProducesResponseType(StatusCodes.Status401Unauthorized)]
[ProducesResponseType(StatusCodes.Status500InternalServerError)]
public async Task<ActionResult<NluTrainingStateDto>> TrainAsync(string projectName)
{
try
{
// Launch a training job.
NluTrainingStateDto result = await _nluService.TrainAsync(projectName);
// Return the correct status based on whether the request was a success.
if (result != null)
{
return Ok(result);
}
else
{
return NotFound();
}
}
catch (ServiceAccessException serviceEx)
{
// TODO: add logging
IError error = _errorFactory.CreateError(ErrorCode.ControllerNotServiced, _exceptionResourceManager.GetString("ConnectionNotServicedExc")!);
ControllerNotServicedException _controllerEx = new(error, serviceEx);
return StatusCode((int)HttpStatusCode.InternalServerError, $"Internal Server Error: {_controllerEx.Message}");
}
}
我尝试使用四种不同的方法提出请求:
POST
、PATCH
、PUT
和GET
。除了 GET
之外,所有这些都失败了,但我相信使用 GET
来满足此请求违反了惯例,并将其保留为最后的手段。我应该注意到,这个特定的函数以前用作 POST
请求
编辑:APIM 是通过 OpenAPI 规范通过 swagger url 创建的。
这是应该通过 APIM 调用的函数(通过 postman 或 swagger 测试它表明该函数可以正常工作)。但是,当 API 本地托管且中间没有 API 网关时,前端 API 调用也会产生
结果401
请验证您在从前端和 APIM 调用时是否传递了正确的 JWT 令牌。我使用下面的代码生成 JWT 令牌并在发出请求时使用相同的代码。
using System;
using System.IdentityModel.Tokens.Jwt;
using System.Security.Claims;
using System.Text;
using Microsoft.IdentityModel.Tokens;
namespace JwtTokenGenerator
{
class Program
{
static void Main(string[] args)
{
var token = GenerateJwtToken();
Console.WriteLine($"Generated JWT Token: {token}");
}
static string GenerateJwtToken()
{
var securityKey = new SymmetricSecurityKey(Encoding.UTF8.GetBytes("<Key>"));
var credentials = new SigningCredentials(securityKey, SecurityAlgorithms.HmacSha256);
var claims = new[]
{
new Claim(JwtRegisteredClaimNames.Sub, "Test Claim"),
new Claim(JwtRegisteredClaimNames.Jti, Guid.NewGuid().ToString())
};
var token = new JwtSecurityToken(
issuer: "<https://sts.windows.net/{tenant_id}/>",
audience: "<audience value>",
claims: claims,
expires: DateTime.Now.AddMinutes(30),
signingCredentials: credentials);
return new JwtSecurityTokenHandler().WriteToken(token);
}
}
}
program.cs
文件中有给定的代码。using _78711442;
using Microsoft.AspNetCore.Authentication.JwtBearer;
using Microsoft.IdentityModel.Tokens;
using System.Text;
var builder = WebApplication.CreateBuilder(args);
builder.Services.AddAuthentication(JwtBearerDefaults.AuthenticationScheme)
.AddJwtBearer(options =>
{
options.TokenValidationParameters = new TokenValidationParameters
{
ValidateIssuer = true,
ValidateAudience = true,
ValidateLifetime = true,
ValidateIssuerSigningKey = true,
ValidIssuer = builder.Configuration["Jwt:Issuer"],
ValidAudience = builder.Configuration["Jwt:Audience"],
IssuerSigningKey = new SymmetricSecurityKey(Encoding.UTF8.GetBytes(builder.Configuration["Jwt:Key"]))
};
});
builder.Services.AddScoped<INluService, NluService>();
builder.Services.AddControllers();
builder.Services.AddEndpointsApiExplorer();
builder.Services.AddSwaggerGen();
var app = builder.Build();
if (app.Environment.IsDevelopment())
{
app.UseSwagger();
app.UseSwaggerUI();
}
app.UseHttpsRedirection();
app.UseAuthentication();
app.UseAuthorization();
app.MapControllers();
app.Run();
access denied, missing subscription key
错误,并且还应在根文件夹中创建 .env。REACT_APP_WEBAPI_ENDPOINT=<endpoint>
REACT_APP_WEBAPI_GATEWAY_SUBSCRIPTION_KEY=<subscription key>
import axios from 'axios';
export const webApiConfig = {
endpointUrl: process.env.REACT_APP_WEBAPI_ENDPOINT,
subscriptionKey: process.env.REACT_APP_WEBAPI_GATEWAY_SUBSCRIPTION_KEY,
};
export const trainProjectAsync = async (projectName) => {
try {
const url = `${webApiConfig.endpointUrl}/WeatherForecast/${projectName}/Train`;
const requestConfig = {
headers: {
Authorization: `Bearer eyJhbGciOi******-FBSYNXiI`,
"Content-Type": "application/json",
"Ocp-Apim-Subscription-Key": webApiConfig.subscriptionKey,
},
};
const response = await axios.post(url, {}, requestConfig);
return response.data;
} catch (error) {
if (axios.isAxiosError(error)) {
throw new Error(`API call axios malfunction code: ${error.code} ${JSON.stringify(error.response)}`);
} else {
throw new Error(`API call stock malfunction, reason: ${error}`);
}
}
};
我能够得到预期的回应。