我做错了什么,它返回此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;
}
答案 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;
}
}
我认为上述政策就是您所追求的。