【问题标题】:User.FindFirst(ClaimTypes.NameIdentifier) - NullReferenceException from Angular but OK in Postman?User.FindFirst(ClaimTypes.NameIdentifier) - 来自 Angular 的 NullReferenceException 但在 Postman 中可以吗?
【发布时间】:2020-09-08 02:25:43
【问题描述】:

我正在尝试在 .NET Core API 和 Angular 应用程序中调试 NullReferenceException,但我没有想法。

我正在尝试更新 User 的属性(“关于”部分)

Update text area screengrab

在后端代码中,在AuthController 中,我有一个创建声明的登录方法,它似乎工作正常:

[HttpPost("login")]
public async Task<IActionResult> Login(LoginViewModel loginViewModel)
{
        // login the user
        var userFromRepo = await _authRepository.Login(loginViewModel.Email.ToLower(), loginViewModel.Password);

        // check that user is logged in
        if (userFromRepo == null)
            return Unauthorized();

        // create claims using user id and main email
        var claims = new[]
        {
            new Claim(ClaimTypes.NameIdentifier, userFromRepo.Id),
            new Claim(ClaimTypes.Name, userFromRepo.MainEmail),
            new Claim(ClaimTypes.Name, userFromRepo.FirstName)
        };

        // generate key from secret token
        var key = new SymmetricSecurityKey(Encoding.UTF8.GetBytes(_config.GetSection("AppSettings:Token").Value));

        // generate hash and credentials
        var cred = new SigningCredentials(key, SecurityAlgorithms.HmacSha512Signature);

        // create token descriptions
        var tokenDescriptor = new SecurityTokenDescriptor
        {
            Subject = new ClaimsIdentity(claims),
            Expires = DateTime.Now.AddDays(1),
            SigningCredentials = cred
        };

        // instantiate token handler
        var tokenHandler = new JwtSecurityTokenHandler();

        // create token
        var token = tokenHandler.CreateToken(tokenDescriptor);

        // write token and return request
        return Ok(new
        {
            token = tokenHandler.WriteToken(token),
        });
}

然后我有一个更新用户属性的方法:

[HttpPut("{id}")]
public async Task<IActionResult> UpdateUser(string id, UserForUpdateDto userForUpdateDto)
{
    // this is a check if user ID that is updating the profile matches the ID in the token
    if (id != User.FindFirst(ClaimTypes.NameIdentifier).Value) 
    {
        return Unauthorized();
    }

    var userFromRepo = await _doMoreRepo.GetUser(id);
    userFromRepo.About = userForUpdateDto.About;
    await _doMoreRepo.UpdateUser(id);
    return NoContent();
}

在调试期间,我收到一条 500 错误消息

System.NullReferenceException:对象引用未设置为对象的实例

在代码行上:

User.FindFirst(ClaimTypes.NameIdentifier).Value

它在 Postman 中运行良好,所以我猜想我没有从 NameIdentifier 得到任何东西,但我不知道为什么?

我只是不知道该往哪里看。

我的前端 Angular 代码如下 - Login 将令牌添加到存储的方法:

login(model: any) {
  return this.http.post(this.url + 'login', model)
  .pipe(
    map((response: any) => {
      const user = response;
      if (user) {
        localStorage.setItem('token', user.token);
        this.decodedToken = this.jwtHelper.decodeToken(user.token);
        console.log('This is decoded token');
        console.log(this.decodedToken);
      }
    })
  );
}

更新配置文件方法:

updateProfile() {
    this.userService.updateUser(this.authService.decodedToken.nameid, this.user).subscribe(next => {
      this.alertify.success('Profile updated');
      this.editForm.reset(this.user);
    }, error => {
      console.log(error);
      this.alertify.error(error);
    });
}

我查看了非常相似的问题here,所以我仔细检查了解决方案,但就我而言,我的app.module.ts 中确实包含tokenGetter()

export function tokenGetter() {
   return localStorage.getItem('token');
}

和导入:

 JwtModule.forRoot({
     config: {
        tokenGetter,
     }
  })

为了进一步调查它并确保我已经缩小到可能的问题区域,在我更换控制器时:

public async Task<IActionResult> UpdateUser(string id, UserForUpdateDto userForUpdateDto)
{
    // this is a check if user ID that is updating the profile matches the ID in the token
    if (id != User.FindFirst(ClaimTypes.NameIdentifier).Value) 
    {
        return Unauthorized();
    }

    var userFromRepo = await _doMoreRepo.GetUser(id);
    userFromRepo.About = userForUpdateDto.About;
    await _doMoreRepo.UpdateUser(id);
    return NoContent();
}

实际值如下:

public async Task<IActionResult> UpdateUser(string id, UserForUpdateDto userForUpdateDto)
{
    if (id != "user ID value") 
    {
        return Unauthorized();
    }

    var userFromRepo = await _doMoreRepo.GetUser(id);
    userFromRepo.About = userForUpdateDto.About;
    await _doMoreRepo.UpdateUser(id);
    return NoContent();
}

这工作正常,没有任何错误,并且属性更新正常。

我错过了什么?

编辑:

这是我发出http.put 请求的updateUser() 方法:

updateUser(id: string, user: User) {
  // console.log('user ID is: ' + id);
  // console.log('User object passed to updateUser() is: ');
  // console.log(user);
  return this.http.put(this.baseUrl + 'user/' + id, user);

}

点击后端UserController.cs中的UpdateUser()

【问题讨论】:

  • 如果User.FindFirst 返回null,使用.Value 会抛出。您还没有发布实际的请求,所以只能猜测这两个请求是不同的。您可以使用例如 Fiddler 或开发人员工具中浏览器的网络选项卡来检查实际发送到服务器的内容。我怀疑 Angular 请求缺少身份验证标头或 cookie,因此 UpdateUser 匿名运行,这意味着没有当前用户
  • @PanagiotisKanavos,感谢您的评论和有用的信息,我想您可能会有所收获。我将尝试对此进行调查并报告。再次感谢您抽出宝贵时间提供帮助。

标签: c# asp.net angular typescript


【解决方案1】:

感谢Panagiotis Kanavos提供的信息,我进一步调查,请求缺少身份验证标头。

因为我用的是JwtModule

export function tokenGetter() {
   return localStorage.getItem('token');
}
 JwtModule.forRoot({
     config: {
        tokenGetter,
     }
  })

我认为我的令牌应该自动添加到标题中,而无需手动添加。

我犯的错误是在JwtModule 配置中没有包含正确的whitelistedDomains 选项,这就是它没有添加到请求标头的原因。

解决方案是将正确的域列入白名单,如下所示:

JwtModule.forRoot({
         config: {
            tokenGetter: tokenGetter,
            whitelistedDomains: ['localhost:5000', 'localhost:5001'],
            blacklistedRoutes: ['localhost:5000/auth']
         }
      })

【讨论】:

    猜你喜欢
    • 2020-03-30
    • 2017-04-05
    • 2018-12-17
    • 2022-11-24
    • 1970-01-01
    • 2013-04-21
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多