Azure APIM 在较大集合的单个 api 调用上缺少订阅密钥错误

问题描述 投票:0回答:1

我有一组 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 创建的。

c# typescript axios azure-api-management
1个回答
0
投票

这是应该通过 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();
  • 您可以通过在请求标头中添加不记名令牌来直接在 APIM 中进行测试。

enter image description here enter image description here

enter image description here

  • 请检查代码中是否正确填充了 .env 中声明的变量,以解决
    access denied, missing subscription key
    错误,并且还应在根文件夹中创建 .env

enter image description here

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}`);
        }
    }
};

我能够得到预期的回应。

enter image description here

© www.soinside.com 2019 - 2024. All rights reserved.