【发布时间】:2020-08-02 13:48:53
【问题描述】:
我做错了什么,它返回了这个 404 “Bad Request-Request Too Long message”。 我的场景如下:我有一个 IdentityServer4 作为 IdentityProvider 和一个使用令牌进行访问的 WebApp。 在 IS4 中,我在 Claims 集合 (Request.User.Claims) 中为用户设置了大约 200 个角色。 当我打开另一个 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' 为空。所以我认为我需要以某种方式将 access_token 提供给 HttpClient 。请参阅下面的代码/设置:
IS4 Startup => 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;
}
【问题讨论】:
标签: identityserver4 core identity user-roles