8wDlpd.png
8wDFp9.png
8wDEOx.png
8wDMfH.png
8wDKte.png

无法检索用于 API 请求中的授权标头的访问令牌

Ewan Brown 2月前

16 0

我提前为这篇很长的文章道歉,只是情况比较特殊,我无法在任何地方找到解决方案。我一直在为公司设计单点登录系统......

我提前为这篇很长的帖子道歉,只是情况比较特殊,我无法在任何地方找到解决方案。

我一直在为我工作的公司设计一个使用 ASP.NET 和 OpenIddict 的单点登录系统。作为这项工作的一部分,我创建了一个新的“管理”区域用于用户管理,它充当与身份提供者分开的客户端应用程序。由于身份提供者可以访问用户数据库,我开发了 API 调用,管理客户端应用程序可以向其发送请求,以便身份提供者代表该客户端检索或更改用户信息,这意味着对用户数据库的访问完全由身份提供者负责,而不是在两个地方存储或访问信息。

目前,发送给身份提供商中用于用户管理的 API 调用的 HttpRequest 在管理客户端是安全的,但在身份提供商端则不是。在管理客户端中,授权策略的使用已到位,并通过标签实现 [Authorize (Policy = "AdminUser")] 。因此,当用户在管理客户端上发出请求时,它会在授权请求之前检查他们是否经过身份验证,以及他们是否具有有效的角色声明。这一切都很好,但是这些 API 调用所在的控制器上的身份提供商端也采用了相同的授权策略,但它会尝试返回登录页面,因为来自管理客户端的请求未经授权。

我认为问题在于我没有正确地将访问令牌与请求一起传回,因此身份提供者 API 调用看不到用户的角色是什么,甚至不知道他们是否经过身份验证。我一直无法弄清楚如何检索它,然后正确地传递它。这是我目前所拥有的:

在 Admin 客户端应用中,HomeController.cs 中的 Index 方法用于在 Admin 登录页面上显示用户。此方法使用该 GetAllUsersAsync() 方法通过对身份提供者 API 调用发出的请求获取所有用户。

[HttpGet("~/")]
public async Task<IActionResult> Index()
{
    List<UserInfoModel> userModel = new();
    var users = await _adminClient.GetAllUsersAsync();

    foreach ( var user in users )
    {
        UserInfoModel userInfo = new();

        userInfo.Id = user.Id;
        userInfo.Role = user.Role;
        userInfo.UserName = user.UserName;
        userInfo.Email = user.Email;

        userModel.Add(userInfo);
    }

    return View(userModel);
}

AdminClient.cs 注册为单例服务,并采用一些参数(主要用于客户端 ID 和机密)。

AdminClient.cs:

private readonly HttpClient client = new();
private readonly string clientId;
private readonly string clientSecret;
private readonly ILogger logger;
private readonly object accessTokenLock = new object();
private DateTime accessTokenExpiry = DateTime.MinValue;
private readonly IHttpContextAccessor _httpContextAccessor;

public AdminClient(string baseAddress, string clientId, string clientSecret, ILogger<AdminClient> logger, IHttpContextAccessor httpContextAccessor)
{
    client.BaseAddress = new Uri(baseAddress);
    this.clientId = clientId;
    this.clientSecret = clientSecret;
    this.logger = logger;
    _httpContextAccessor = httpContextAccessor;
}

程序.cs:

builder.Services.AddSingleton<IAdminClient>(serviceProvider => new AdminClient(
    clientDetails.GetSection("IssuerUrl").Value?.ToString(),
    clientDetails.GetSection("ClientId").Value?.ToString(),
    clientDetails.GetSection("ClientSecret").Value?.ToString(),
    serviceProvider.GetRequiredService<ILogger<AdminClient>>(),
    new HttpContextAccessor()));

GetAllUsersAsync() 来自 AdminClient.cs。此方法尝试从 Http 上下文获取用户的授权码,然后使用授权码通过该 GetAccessTokenAsync() 方法获取访问令牌(本文将进一步介绍)。然后,它会向身份提供者中的用户管理控制器创建一个 Http 请求,并将访问令牌包含在授权标头中。它会发送此请求,然后该方法的其余部分会在请求成功后反序列化 JSON。

public async Task<List<UserDTO>> GetAllUsersAsync()
{
    try
    {
        // Get authorisation code
        string? authCode = _httpContextAccessor?.HttpContext?.Request.Query["code"];
        string? accessToken = await GetAccessTokenAsync(authCode);

        HttpRequestMessage request = new()
        {
            Method = HttpMethod.Get,
            RequestUri = new Uri($"useradministration/getallusersdetailsandclaims", UriKind.Relative),
            Headers =
            {
                Authorization = new AuthenticationHeaderValue("Bearer", accessToken)
            }
        };

        var response = await client.SendAsync(request);
        response.EnsureSuccessStatusCode();

        var content = await response.Content.ReadAsStringAsync();
        var userData = System.Text.Json.JsonSerializer.Deserialize<List<UserDTO>>(content,
            new JsonSerializerOptions
            {
                PropertyNamingPolicy = JsonNamingPolicy.CamelCase
            });

        return userData;
    }
    catch (Exception ex)
    {
        throw new InvalidOperationException(ex.Message);
    }
}

GetAccessTokenAsync() 方法也位于 AdminClient.cs 类中。它向身份提供者的令牌端点创建一个请求,并在请求的内容中包含授权类型(使用授权码流程)、通过参数传递的授权码以及客户端的 ID 和密钥。然后,它发送请求,如果成功,则反序列化 JSON 响应以提取访问令牌并将其返回,以便在请求中使用 GetAllUsersAsync()

private async Task<string> GetAccessTokenAsync(string authCode)
{
    HttpRequestMessage request = new()
    {
        Method = HttpMethod.Post,
        RequestUri = new Uri($"{client.BaseAddress}connect/token"),
        Content = new FormUrlEncodedContent(new[]
        {
            new KeyValuePair<string, string>("grant_type", "authorization_code"),
            new KeyValuePair<string, string>("code", authCode),
            new KeyValuePair<string, string>("client_id", clientId), 
            new KeyValuePair<string, string>("client_secret", clientSecret)
        })
    };
    try
    {
        HttpResponseMessage? response = await client.SendAsync(request);
        response.EnsureSuccessStatusCode();

        string responseContent = await response.Content.ReadAsStringAsync();
        JObject json = JObject.Parse(responseContent);
        string accessToken = json["access_token"].ToString();
        return accessToken;
    }
    catch (Exception ex) 
    {
        throw new Exception(ex.Message);
    }
}

目前,我 null 从 Http 上下文查询中获取一个值以获取授权码,因此访问令牌未从 返回 GetAccessTokenAsync() ,导致 中的请求错误或未经授权 GetAllUsersAsync() 。最重要的是,即使我确实获得了授权码,我仍然不确定这是否是正确的做法。如果我能得到一些关于我所缺少的东西的建议,或者如果我需要以不同的方式完成这个过程,那就太好了。

如果需要更多信息或背景信息,请告诉我,我会发送您需要的任何信息。

先感谢您。

帖子版权声明 1、本帖标题:无法检索用于 API 请求中的授权标头的访问令牌
    本站网址:http://xjnalaquan.com/
2、本网站的资源部分来源于网络,如有侵权,请联系站长进行删除处理。
3、会员发帖仅代表会员个人观点,并不代表本站赞同其观点和对其真实性负责。
4、本站一律禁止以任何方式发布或转载任何违法的相关信息,访客发现请向站长举报
5、站长邮箱:yeweds@126.com 除非注明,本帖由Ewan Brown在本站《asp.net》版块原创发布, 转载请注明出处!
最新回复 (0)
  • 看来您正在使用 JWT 令牌。SP 收到的解密 JWT 令牌中是否有刷新令牌?

  • @LajosArpad 我还没有真正设置刷新令牌,但我很快就会考虑这样做。实施一种请求刷新令牌而不是访问令牌的方法会是更好的解决方案吗?我理解这就是延长访问令牌有效性的方法。

  • 基本上,JWT 令牌必须是短期令牌(可能已经过期,特别是如果只发送一次),并且您可能有一个或多个刷新令牌作为编码到 JWT 中的字段。因此,当您从 SP 向 IDP 发出请求时,您会发回一个刷新令牌,该令牌已用完,IDP 将通过刷新令牌对您进行身份验证,使其失效并向您发回一个新的刷新令牌,您可以在下次请求时使用。

返回
作者最近主题: