IdentityServer4显示404-请求太长

时间:2020-07-28 07:54:45

标签: identityserver4 core identity user-roles

我做错了什么,它返回此404“错误的请求-请求太长消息”。 我的情况如下:我有一个IdentityServer4(充当IdentityProvider)和一个WebApp(使用令牌进行访问)。 在IS4中,我为用户设置了大约200个角色到Claims集合(Request.User.Claims)中。 当我打开另一个WebApp时,错误弹出。

有人可以告诉我如何正确发送UserRoles以便在WebApp中使用。因此,我可以使用User.IsInRole({some-role})。 我只看到一些示例,这些示例传递了一些角色,但并没有达到我的需要。

当您需要有关某事的更多信息时,请告诉我您需要知道什么才能回答我的问题?

我使用以下站点来设置IS4:

https://deblokt.com/2020/01/24/04-part-1-identityserver4-asp-net-core-identity-net-core-3-1/ https://github.com/KevinDockx/SecuringAspNetCore2WithOAuth2AndOIDC来自PluralSite课程。

更新:我做错了我的姿势。在代码下面,我需要获取AuthControler API授权工作,该工作卡在BearerTokenHandler中,而'expires_at'为null。所以我认为我需要以某种方式将access_token赋予HttpClient。请参阅下面的代码/设置:

IS4启动=>最后的ConfigurationService:

    //Set named HttpClient settings for API to get roles of user
    services.AddHttpContextAccessor();
    services.AddTransient<BearerTokenHandler>();
    services.AddHttpClient("AuthClient", client =>
    {
        client.BaseAddress = new Uri("https://localhost:44318/");
        client.DefaultRequestHeaders.Clear();
        client.DefaultRequestHeaders.Add(HeaderNames.Accept, "application/json");
    }).AddHttpMessageHandler<BearerTokenHandler>();

CustomClaimPrincipal:

    public class CustomClaimsPrincipal : ClaimsPrincipal
    {
        private readonly IHttpClientFactory _httpClientFactory;
        private readonly IHttpContextAccessor _httpContextAccessor;

        public CustomClaimsPrincipal(IHttpContextAccessor httpContextAccessor, IHttpClientFactory httpClientFactory, IPrincipal principal) : base(principal)
        {
            _httpContextAccessor = httpContextAccessor ??
               throw new ArgumentNullException(nameof(httpContextAccessor));
            _httpClientFactory = httpClientFactory ??
                 throw new ArgumentNullException(nameof(httpClientFactory));
        }

        public override bool IsInRole(string role)
        {
            var hasRole = base.IsInRole(role);

            if (!hasRole)
            {
                // get the saved identity token
                var identityToken = _httpContextAccessor.HttpContext.GetTokenAsync(OpenIdConnectParameterNames.IdToken);

                var httpClient = _httpClientFactory.CreateClient("AuthClient");
                var request = new HttpRequestMessage(HttpMethod.Get, "/api/auth/isinrole");
                var response = httpClient.SendAsync(request, HttpCompletionOption.ResponseHeadersRead).Result;
                
                if (response.IsSuccessStatusCode)
                {
                    using (var responseStream = response.Content.ReadAsStreamAsync().Result)
                    {
                        var roles = JsonSerializer.DeserializeAsync<List<string>>(responseStream).Result;
                        hasRole = roles.Any(perm => perm == role);
                    }
                }
                else if (response.StatusCode == System.Net.HttpStatusCode.Unauthorized || response.StatusCode == System.Net.HttpStatusCode.Forbidden)
                {
                    hasRole = false;// RedirectToAction("AccessDenied", "Authorization");
                }
            }

            return hasRole;
        }
    }

IS4项目中的AuthController:

ApiController]
[Authorize]
public class AuthController : ControllerBase
{
    private readonly IdentityContext _context;

    public AuthController(IdentityContext context)
    {
        _context = context ?? throw new ArgumentNullException(nameof(context));
    }

    [HttpGet()]
    [Route("api/auth/isinrole")]
    public IActionResult IsInRole()
    {
        if (User.FindFirst("sub")?.Value != null)
        {
            var userID = Guid.Parse(User.FindFirst("sub")?.Value);
            var groupsRoles = _context.GroupRoles.Where(c => _context.UserGroups.Where(c => c.UserId == userID).Select(c => c.Group.Id).Any(d => d.Equals(c.GroupId))).Select(c => c.Role.Name);
            var userRoles = _context.UserRoles.Where(c => c.UserId == userID).Select(c => c.Role.Name);
            var roles = groupsRoles.Union(userRoles).Distinct().ToList();

            return Ok(JsonSerializer.Serialize(roles));
        }

        return Forbid();

    }
}

BearerTokenHandler:

public class BearerTokenHandler : DelegatingHandler
{
    private readonly IHttpContextAccessor _httpContextAccessor;
    private readonly IHttpClientFactory _httpClientFactory;

    public BearerTokenHandler(IHttpContextAccessor httpContextAccessor,
                IHttpClientFactory httpClientFactory)
    {
        _httpContextAccessor = httpContextAccessor ??
            throw new ArgumentNullException(nameof(httpContextAccessor));
        _httpClientFactory = httpClientFactory ??
                throw new ArgumentNullException(nameof(httpClientFactory));
    }
        

    protected override async Task<HttpResponseMessage> SendAsync(
        HttpRequestMessage request, 
        CancellationToken cancellationToken)
    {
        var accessToken = await GetAccessTokenAsync();

        if (!string.IsNullOrWhiteSpace(accessToken))
        {
            request.SetBearerToken(accessToken);
        }

        return await base.SendAsync(request, cancellationToken);
    }

    public async Task<string> GetAccessTokenAsync()
    {
        // get the expires_at value & parse it
        var expiresAt = await _httpContextAccessor.HttpContext.GetTokenAsync("expires_at");
        var expiresAtAsDateTimeOffset = DateTimeOffset.Parse(expiresAt, CultureInfo.InvariantCulture);

        if ((expiresAtAsDateTimeOffset.AddSeconds(-60)).ToUniversalTime() > DateTime.UtcNow)
            return await _httpContextAccessor.HttpContext.GetTokenAsync(OpenIdConnectParameterNames.AccessToken); // no need to refresh, return the access token

        var idpClient = _httpClientFactory.CreateClient("IDPClient");

        // get the discovery document
        var discoveryReponse = await idpClient.GetDiscoveryDocumentAsync();

        // refresh the tokens
        var refreshToken = await _httpContextAccessor.HttpContext.GetTokenAsync(OpenIdConnectParameterNames.RefreshToken);

        var refreshResponse = await idpClient.RequestRefreshTokenAsync(new RefreshTokenRequest {
                Address = discoveryReponse.TokenEndpoint,
                ClientId = "mvc",
                ClientSecret = "secret",
                RefreshToken = refreshToken
            });

        // store the tokens             
        var updatedTokens = new List<AuthenticationToken>();
        updatedTokens.Add(new AuthenticationToken {
            Name = OpenIdConnectParameterNames.IdToken,
            Value = refreshResponse.IdentityToken
        });
        updatedTokens.Add(new AuthenticationToken {
            Name = OpenIdConnectParameterNames.AccessToken,
            Value = refreshResponse.AccessToken
        });
        updatedTokens.Add(new AuthenticationToken {
            Name = OpenIdConnectParameterNames.RefreshToken,
            Value = refreshResponse.RefreshToken
        });
        updatedTokens.Add(new AuthenticationToken {
            Name = "expires_at",
            Value = (DateTime.UtcNow + TimeSpan.FromSeconds(refreshResponse.ExpiresIn)).
                    ToString("o", CultureInfo.InvariantCulture)
        });

        // get authenticate result, containing the current principal & properties
        var currentAuthenticateResult = await _httpContextAccessor.HttpContext.AuthenticateAsync(CookieAuthenticationDefaults.AuthenticationScheme);

        // store the updated tokens
        currentAuthenticateResult.Properties.StoreTokens(updatedTokens);

        // sign in
        await _httpContextAccessor.HttpContext.SignInAsync(CookieAuthenticationDefaults.AuthenticationScheme, currentAuthenticateResult.Principal, currentAuthenticateResult.Properties);

        return refreshResponse.AccessToken;
    }

1 个答案:

答案 0 :(得分:0)

如果您需要为给定的用户添加200个角色,那么您肯定会做错。角色列表不应包含在令牌中,我想一个用户永远都不属于200个角色的一部分?只有一点?您应该只将给定用户实际使用的角色包括在令牌中。

这段视频Implementing authorization in web applications and APIs是一个很好的起点

例如,在客户应用程序中,如果您需要在登录时查找其他信息,则可以使用声明转换,例如:

public class BonusLevelClaimTransformation : IClaimsTransformation
{
    public Task<ClaimsPrincipal> TransformAsync(ClaimsPrincipal principal)
    {
        if (!principal.HasClaim(c => c.Type == "bonuslevel"))
        {
            //Lookup bonus level.....
            principal.Identities.First().AddClaim(new Claim("bonuslevel", "12345"));
        }
        return Task.FromResult(principal);
    }
}

或者,您可以在客户端中使用策略(不是基于角色)授权概念,并创建一个处理程序,该处理程序将为您查找其他权限,例如:

 options.AddPolicy("ViewReports", policy =>
   policy.Requirements.Add(new CanViewReportsRequirement(startHour: 9,
                                                         endHour: 18)));


public class CanViewReportsRequirement : IAuthorizationRequirement
{
    public int StartHour { get; }
    public int EndHour { get; }

    public CanViewReportsRequirement(int startHour, int endHour)
    {
        StartHour = startHour;
        EndHour = endHour;
    }
}

//You can have one more handler connected to the requirement above
public class CheckIfAccountantHandler : AuthorizationHandler<CanViewReportsRequirement>
{
    protected override Task HandleRequirementAsync(AuthorizationHandlerContext context,
                                                   CanViewReportsRequirement requirement)
    {
        
      bool result = CallCheckIfAccountantService();

      if(result)
                context.Succeed(requirement);

        return Task.CompletedTask;
    }
}

我认为上述政策就是您所追求的。

相关问题