ASP.NET Web API の JWT 認証 質問する

ASP.NET Web API の JWT 認証 質問する

Web API アプリケーションで JWT ベアラー トークン (JSON Web トークン) をサポートしようとしているのですが、行き詰まっています。

.NET Core と OWIN アプリケーションがサポートされているようです。
現在、アプリケーションを IIS でホストしています。

<authentication>アプリケーションでこの認証モジュールを実現するにはどうすればよいですか?フォーム/Windows 認証を使用する方法と同様の構成を使用する方法はありますか?

ベストアンサー1

私はこの質問に答えました:ASP.NET Web API をセキュリティで保護する方法4年前にHMACを使用。

現在、セキュリティに関しては多くのことが変化しており、特に JWT が普及しつつあります。この回答では、OWIN、Oauth2、ASP.NET Identity などのジャングルで迷子にならないように、できるだけシンプルかつ基本的な方法で JWT を使用する方法を説明します。

JWT トークンについて知らない場合は、以下を確認する必要があります。

https://www.rfc-editor.org/rfc/rfc7519

基本的に、JWT トークンは次のようになります。

<base64-encoded header>.<base64-encoded claims>.<base64-encoded signature>

例:

eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJ1bmlxdWVfbmFtZSI6ImN1b25nIiwibmJmIjoxNDc3NTY1NzI0LCJleHAiOjE0Nzc1NjY5MjQsImlhdCI6MTQ3NzU2NTcyNH0.6MzD1VwA5AcOcajkFyKhLYybr3h13iZjDyHm9zysDFQ

JWT トークンには 3 つのセクションがあります。

  1. ヘッダー: Base64でエンコードされたJSON形式
  2. クレーム: Base64 でエンコードされた JSON 形式。
  3. 署名: Base64 でエンコードされたヘッダーとクレームに基づいて作成され、署名されます。

ウェブサイトをご利用の場合翻訳元上記のトークンを使用してトークンをデコードすると、以下のように表示されます。

jwt.io のスクリーンショット。生の jwt ソースとそれが表すデコードされた JSON が表示されます。

技術的には、JWT は、ヘッダーで指定されたセキュリティ アルゴリズム (例: HMACSHA256) を使用して、ヘッダーとクレームから署名された署名を使用します。したがって、クレームに機密情報を保存する場合は、JWT を HTTPS 経由で転送する必要があります。

現在、JWT 認証を使用するために、レガシー Web API システムがある場合、OWIN ミドルウェアは実際には必要ありません。シンプルな概念は、JWT トークンを提供する方法と、リクエストが来たときにトークンを検証する方法です。それだけです。

の中に私が作成したデモ(github)、JWT トークンを軽量に保つために、 と のみを保存しますusernameexpiration timeただし、この方法では、ロール認証などを行う場合、ロールなどの情報を追加するために新しいローカル ID (プリンシパル) を再構築する必要があります。ただし、JWT にさらに情報を追加するかどうかはあなた次第です。非常に柔軟性があります。

OWIN ミドルウェアを使用する代わりに、コントローラー アクションを使用して JWT トークン エンドポイントを提供することもできます。

public class TokenController : ApiController
{
    // This is naive endpoint for demo, it should use Basic authentication
    // to provide token or POST request
    [AllowAnonymous]
    public string Get(string username, string password)
    {
        if (CheckUser(username, password))
        {
            return JwtManager.GenerateToken(username);
        }

        throw new HttpResponseException(HttpStatusCode.Unauthorized);
    }

    public bool CheckUser(string username, string password)
    {
        // should check in the database
        return true;
    }
}

これは単純なアクションです。本番環境では、POST リクエストまたは基本認証エンドポイントを使用して JWT トークンを提供する必要があります。

に基づいてトークンを生成する方法はusername?

System.IdentityModel.Tokens.Jwtトークンを生成するには、Microsoft から呼び出された NuGet パッケージを使用するか、必要に応じて別のパッケージを使用することもできます。デモでは、HMACSHA256以下を使用しますSymmetricKey

/// <summary>
/// Use the below code to generate symmetric Secret Key
///     var hmac = new HMACSHA256();
///     var key = Convert.ToBase64String(hmac.Key);
/// </summary>
private const string Secret = "db3OIsj+BXE9NZDy0t8W3TcNekrF+2d/1sFnWG4HnV8TZY30iTOdtVWJG8abWvB1GlOgJuQZdcF2Luqm/hccMw==";

public static string GenerateToken(string username, int expireMinutes = 20)
{
    var symmetricKey = Convert.FromBase64String(Secret);
    var tokenHandler = new JwtSecurityTokenHandler();

    var now = DateTime.UtcNow;
    var tokenDescriptor = new SecurityTokenDescriptor
    {
        Subject = new ClaimsIdentity(new[]
        {
            new Claim(ClaimTypes.Name, username)
        }),

        Expires = now.AddMinutes(Convert.ToInt32(expireMinutes)),
        
        SigningCredentials = new SigningCredentials(
            new SymmetricSecurityKey(symmetricKey), 
            SecurityAlgorithms.HmacSha256Signature)
    };

    var stoken = tokenHandler.CreateToken(tokenDescriptor);
    var token = tokenHandler.WriteToken(stoken);

    return token;
}

JWT トークンを提供するエンドポイントが完了しました。

リクエストが来たときに JWT を検証するにはどうすればよいですか?

の中にデモ、私は(認証フィルタの詳細についてはJwtAuthenticationAttributeIAuthenticationFilterここ)。

この属性を使用すると、任意のアクションを認証できます。そのアクションにこの属性を設定するだけです。

public class ValueController : ApiController
{
    [JwtAuthentication]
    public string Get()
    {
        return "value";
    }
}

WebAPI のすべての受信リクエストを検証したい場合は、OWIN ミドルウェアまたは DelegateHander を使用することもできます (コントローラーやアクションに固有ではありません)

以下は認証フィルターのコアメソッドです。

private static bool ValidateToken(string token, out string username)
{
    username = null;

    var simplePrinciple = JwtManager.GetPrincipal(token);
    var identity = simplePrinciple.Identity as ClaimsIdentity;

    if (identity == null || !identity.IsAuthenticated)
        return false;

    var usernameClaim = identity.FindFirst(ClaimTypes.Name);
    username = usernameClaim?.Value;

    if (string.IsNullOrEmpty(username))
       return false;

    // More validate to check whether username exists in system

    return true;
}

protected Task<IPrincipal> AuthenticateJwtToken(string token)
{
    string username;

    if (ValidateToken(token, out username))
    {
        // based on username to get more information from database 
        // in order to build local identity
        var claims = new List<Claim>
        {
            new Claim(ClaimTypes.Name, username)
            // Add more claims if needed: Roles, ...
        };

        var identity = new ClaimsIdentity(claims, "Jwt");
        IPrincipal user = new ClaimsPrincipal(identity);

        return Task.FromResult(user);
    }

    return Task.FromResult<IPrincipal>(null);
}

ワークフローでは、JWT ライブラリ (上記の NuGet パッケージ) を使用して JWT トークンを検証し、戻りますClaimsPrincipal。システムにユーザーが存在するかどうかを確認するなど、さらに検証を実行したり、必要に応じて他のカスタム検証を追加したりできます。

JWT トークンを検証し、プリンシパルを取得するコード:

public static ClaimsPrincipal GetPrincipal(string token)
{
    try
    {
        var tokenHandler = new JwtSecurityTokenHandler();
        var jwtToken = tokenHandler.ReadToken(token) as JwtSecurityToken;

        if (jwtToken == null)
            return null;

        var symmetricKey = Convert.FromBase64String(Secret);

        var validationParameters = new TokenValidationParameters()
        {
            RequireExpirationTime = true,
            ValidateIssuer = false,
            ValidateAudience = false,
            IssuerSigningKey = new SymmetricSecurityKey(symmetricKey)
        };

        SecurityToken securityToken;
        var principal = tokenHandler.ValidateToken(token, validationParameters, out securityToken);

        return principal;
    }
    catch (Exception)
    {
        //should write log
        return null;
    }
}

JWT トークンが検証され、プリンシパルが返された場合は、新しいローカル ID を構築し、そこにさらに情報を入力することで、ロールの承認を確認する必要があります。

config.Filters.Add(new AuthorizeAttribute());リソースへの匿名リクエストを防ぐために、グローバル スコープで (デフォルトの承認)を追加することを忘れないでください。

Postmanを使ってテストすることができますデモ:

トークンをリクエストします (上で述べたように、デモ用です):

GET http://localhost:{port}/api/token?username=cuong&password=1

承認されたリクエストのヘッダーに JWT トークンを配置します。例:

GET http://localhost:{port}/api/value

Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJ1bmlxdWVfbmFtZSI6ImN1b25nIiwibmJmIjoxNDc3NTY1MjU4LCJleHAiOjE0Nzc1NjY0NTgsImlhdCI6MTQ3NzU2NTI1OH0.dSwwufd4-gztkLpttZsZ1255oEzpWCJkayR_4yvNL1s

デモはここからご覧いただけます:https://github.com/cuongle/WebApi.Jwt

おすすめ記事