【发布时间】:2021-09-16 00:31:00
【问题描述】:
我遇到以下情况,非常感谢您的帮助:
我在我的用户界面(Blazor 服务器应用程序)中使用 JSON Web 令牌 (jwtbearer) 进行身份验证和授权,以访问我的 Web API (Asp.net core 5.0)。
我尝试从按 UserId 过滤的 API 返回数据(我在创建令牌时也将其添加为 NameIdentifier)。然后我注意到,点击控制器时从ControllerBase.User 返回的ClaimsPrincipal 的声明与我的令牌不同。
我有两个NameIdentifier 声明,并且“子”丢失了。相反,看起来 sub 已更改为第二个 NameIdentifier。
Debugging Screenshot - Claims of this.User in Controller
但是,当我随请求一起发送时,令牌的有效负载似乎是正确的。这是我的令牌的有效负载:
{
"sub": "admin@caliprog.com",
"jti": "15c23576-9f5b-4f81-a985-ab236830c7b5",
"http://schemas.xmlsoap.org/ws/2005/05/identity/claims/nameidentifier": "9b437d6c-8fb8-455d-87f5-986d36b26dcf",
"http://schemas.microsoft.com/ws/2008/06/identity/claims/role": "Administrator",
"exp": 1625472724,
"iss": "test.com",
"aud": "test.com"
}
显然我在某处犯了错误,但我不知道错误在哪里。如果有人能指出我的方向,那就太好了。我很高兴自己做更多的研究,但我不知道从哪里开始。
以下是一些可能相关的代码部分,如果我遗漏了任何重要的内容,请告诉我:
我的 GetRequest(它有效,但我想用 var userId = this.User.FindFirst(ClaimTypes.NameIdentifier).Value; 替换 this.User.Claims.ToList()[2].Value)
[HttpGet]
public async Task<IActionResult> GetWorkouts()
{
try
{
_logger.LogInfo(LogMessages.AttemptMsg, ControllerContext);
var userId = this.User.Claims.ToList()[2].Value;
var workouts = await _workoutRepository.FindAllByUser(userId);
var response = _mapper.Map<IList<WorkoutDTO>>(workouts);
_logger.LogInfo(LogMessages.SuccessActionMsg,ControllerContext);
return Ok(response);
}
catch (Exception e)
{
return _logger.LogInternalErrorResult(e, ControllerContext);
}
}
UsersController -> 登录并生成令牌:
[Route("login")]
[AllowAnonymous]
[HttpPost]
[ProducesResponseType(StatusCodes.Status200OK)]
[ProducesResponseType(StatusCodes.Status401Unauthorized)]
[ProducesResponseType(StatusCodes.Status500InternalServerError)]
public async Task<IActionResult> Login([FromBody] UserDTO userDTO)
{
try
{
var username = userDTO.EmailAddress;
var password = userDTO.Password;
_logger.LogInfo(string.Concat(LogMessages.AttemptMsg, $"With username: {username}."), ControllerContext);
var result = await _signInManager.PasswordSignInAsync(username, password, false, false);
if (result.Succeeded)
{
var user = await _userManager.FindByNameAsync(username);
var tokenstring = await GenerateJSONWebToken(user);
_logger.LogInfo(LogMessages.SuccessActionMsg, ControllerContext);
return Ok(new { token = tokenstring});
}
_logger.LogInfo(string.Concat($"{username}: ", LogMessages.NotAuthenticated), ControllerContext);
return Unauthorized(userDTO);
}
catch (Exception e)
{
return _logger.LogInternalErrorResult(e, ControllerContext);
}
}
private async Task<string> GenerateJSONWebToken(IdentityUser user)
{
var securityKey = new SymmetricSecurityKey(Encoding.UTF8.GetBytes(_config["Jwt:Key"]));
var credentials = new SigningCredentials(securityKey, SecurityAlgorithms.HmacSha256);
var claims = new List<Claim>
{
new Claim(JwtRegisteredClaimNames.Sub,user.Email),
new Claim(JwtRegisteredClaimNames.Jti,Guid.NewGuid().ToString()),
new Claim(ClaimTypes.NameIdentifier,user.Id)
};
var roles = await _userManager.GetRolesAsync(user);
claims.AddRange(roles.Select(r => new Claim(ClaimsIdentity.DefaultRoleClaimType, r)));
var token = new JwtSecurityToken(
_config["Jwt:Issuer"],
_config["Jwt:Issuer"],
claims,
null,
expires: DateTime.Now.AddHours(2),
signingCredentials: credentials);
return new JwtSecurityTokenHandler().WriteToken(token);
}
}
UI 基础存储库
public async Task<IList<T>> Get(string url)
{
var request = new HttpRequestMessage(HttpMethod.Get, url);
var client = _client.CreateClient();
client.DefaultRequestHeaders.Authorization =
new AuthenticationHeaderValue("bearer", await GetBearerToken());
HttpResponseMessage response = await client.SendAsync(request);
if (response.StatusCode == System.Net.HttpStatusCode.OK)
{
var content = await response.Content.ReadAsStringAsync();
return JsonConvert.DeserializeObject<List<T>>(content);
}
return null;
}
private async Task<string> GetBearerToken()
{
return await _localStorage.GetItemAsync<string>("authToken");
}
API AuthenticationStateProvider
public async Task LoggedIn()
{
var savedToken = await _localStorage.GetItemAsync<string>("authToken");
var tokenContent = _tokenHandler.ReadJwtToken(savedToken);
var claims = ParseClaims(tokenContent);
var user = new ClaimsPrincipal(new ClaimsIdentity(claims, "jwt"));
var authState = Task.FromResult(new AuthenticationState(user));
NotifyAuthenticationStateChanged(authState);
}
public void LoggedOut()
{
var nobody = new ClaimsPrincipal(new ClaimsIdentity());
var authState = Task.FromResult(new AuthenticationState(nobody));
NotifyAuthenticationStateChanged(authState);
}
private static IList<Claim> ParseClaims(JwtSecurityToken tokenContent)
{
var claims = tokenContent.Claims.ToList();
claims.Add(new Claim(ClaimTypes.Name, tokenContent.Subject));
return claims;
}
以下是一些项目细节供参考:
API
Microsoft.AspNetCore.Authentication.JwtBearer 版本="5.0.5"
Microsoft.AspNetCore.Diagnostics.EntityFrameworkCore 版本="5.0.5"
Microsoft.AspNetCore.Identity.EntityFrameworkCore 版本="5.0.5"
Microsoft.AspNetCore.Identity.UI 版本="5.0.4"
Microsoft.AspNetCore.Mvc.NewtonsoftJson Version="5.0.6"
UI
Blazored.LocalStorage 版本="4.0.0"
Newtonsoft.Json 版本="13.0.1"
System.IdentityModel.Tokens.Jwt 版本="6.11.0"
【问题讨论】:
标签: c# asp.net-core jwt blazor blazor-server-side