web-dev-qa-db-ja.com

すべてのコントローラーメソッドでJWTからuserIdを取得しますか?

認証と承認にJWTを使用するCore 2.0 Web APIプロジェクトを作成しています。保護したいコントローラーメソッドはすべてAuthorize属性で装飾されています。

これは機能しています。 BearerヘッダーでJWTを渡すと、200を取得します。JWTを渡せない場合は、401を取得します。すべて動作しています。私のJWTでは、認証時に「UserId」フィールドにユーザーIDを保存しました。

var claimsdata = new[] {
                    new Claim("UserId", user.Id.ToString()),

次に、拡張メソッドがあります:

public static string GetUserId(this IPrincipal user)
        {
            if (user == null)
                return string.Empty;

            var identity = (ClaimsIdentity)user.Identity;
            IEnumerable<Claim> claims = identity.Claims;
            return claims.FirstOrDefault(s => s.Type == "UserId")?.Value;
        }

私のコントローラーメソッドでは、「承認」を使用して、ユーザーのIDが必要になることがよくあります。そこで、GetUserIdメソッドを呼び出します。これは機能します。ただし、これがトークンからIDを取得する最善の方法であるかどうかはわかりません。

int.TryParse(User.GetUserId(), out _userId);

すべてのコントローラーでそのコードを使用する必要があります。私はそれが間違っていると思うので、コンストラクタでそれを行うことはできません。

私はここで正しいことをしていますか?

10
Craig

ControllerBaseには、UserのタイプであるClaimsPrincipalプロパティが含まれています

User.Claimsでユーザーのクレームにアクセスできます。IPrincipalは必要ありません。

GetUserIdメソッドをprotectedとして含むベースコントローラを作成します。

public abstract class BaseController : Controller
{        
    protected int GetUserId()
    {
        return int.Parse(this.User.Claims.First(i => i.Type == "UserId").Value);
    }
}

そして、すべてのコントローラがこれから継承し、すべてのコントローラがUserIdにアクセスできるようになりました

14
Hesam Faridmehr

最初に、IUserProviderインターフェースをIHttpContextAccessorインジェクションで作成して、単体テストでこれらのインターフェースのモックを作成します。

   public interface IUserProvider
   {
        string GetUserId();
   }

実装より

    public class UserProvider : IUserProvider
    {
        private readonly IHttpContextAccessor _context;

        public UserProvider (IHttpContextAccessor context)
        {
            _context = context ?? throw new ArgumentNullException(nameof(context));
        }

        public string GetUserId()
        {
            return _context.HttpContext.User.Claims
                       .First(i => i.Type == ClaimTypes.NameIdentifier).Value;
        }
    }

したがって、継承なしでコントローラーでインターフェースIUserProviderを使用できます

    [Authorize]
    [ApiController]
    public class MyController : ControllerBase
    {        
        private readonly IUserProvider _userProvider;

        public MyController(IUserProvider userProvider)
        {            
            _userProvider = userProvider ?? throw new ArgumentNullException(nameof(userProvider ));
        }

        [HttpGet]
        [Route("api/My/Something")]
        public async Task<ActionResult> GetSomething()
        {
            try
            {
                var userId= _userProvider.GetUserId();
            }
        }
     }
2

また使用できます

延長方法

このような

public static long GetUserID(this ClaimsPrincipal User)
{
   return long.Parse(User.Claims.First(i => i.Type == "http://schemas.xmlsoap.org/ws/2005/05/identity/claims/nameidentifier").Value);
}

このようにコントローラに実装します

[HttpDelete("DeleteAddress")]
public async Task<IActionResult> DeleteAddress([FromQuery] long AddressID)
{
   try
   {
      long userID = this.User.GetUserID();
      await _addressService.Delete(userID, AddressID);
      return Ok();
   }
   catch (Exception err)
   {
      return Conflict(err.Message);
   }     
}

お役に立てば幸いです

1
Akbar Asghari