1. ホーム
  2. c#

[解決済み] .NET Core 2.0上のJWT

2023-06-25 06:58:19

質問

私は、DotNet core 2.0 (本日最終リリースに到達) で JWT を動作させるために、かなりの冒険をしてきました。 そこには トン のドキュメントがありますが、すべてのサンプルコードは非推奨のAPIを使用しているようで、Coreに新しく入ってきたため、正確にどのように実装することになっているのかを把握するのは、本当にめまいがするような状態です。Joseを使ってみましたが、app. UseJwtBearerAuthenticationは非推奨となっており、次に何をすべきかのドキュメントがありません。

誰か dotnet core 2.0 を使用するオープンソースプロジェクトで、認可ヘッダーから JWT を単純に解析し、HS256 エンコード JWT トークンのリクエストを認可することができるものはないでしょうか?

以下のクラスは例外をスローしませんが、どのリクエストも認証されず、何の表示も得られません。 なぜ の表示もありません。 レスポンスは空の 401 で、これは例外がないことを示しますが、シークレットがマッチしていないことを示します。

1つ奇妙なことは、私のトークンは HS256 アルゴリズムで暗号化されていますが、そのアルゴリズムを強制的に使用するように指示するインジケータがどこにも見当たりません。

私がこれまでに持っているクラスは以下のとおりです。

using System;
using System.Collections.Generic;
using System.IO;
using Microsoft.AspNetCore.Authentication;
using Microsoft.AspNetCore.Authentication.JwtBearer;
using Microsoft.AspNetCore.Builder;
using Microsoft.AspNetCore.Hosting;
using Microsoft.AspNetCore.Http;
using Microsoft.Extensions.Configuration;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Net.Http.Headers;
using Newtonsoft.Json.Linq;
using Microsoft.IdentityModel.Tokens;
using System.Text;

namespace Site.Authorization
{
    public static class SiteAuthorizationExtensions
    {
        public static IServiceCollection AddSiteAuthorization(this IServiceCollection services)
        {
            var signingKey = new SymmetricSecurityKey(Encoding.ASCII.GetBytes("SECRET_KEY"));

            var tokenValidationParameters = new TokenValidationParameters
            {
                // The signing key must match!
                ValidateIssuerSigningKey = true,
                ValidateAudience = false,
                ValidateIssuer = false,
                IssuerSigningKeys = new List<SecurityKey>{ signingKey },


                // Validate the token expiry
                ValidateLifetime = true,
            };

            services.AddAuthentication(options =>
            {
                options.DefaultAuthenticateScheme = JwtBearerDefaults.AuthenticationScheme;
                options.DefaultChallengeScheme = JwtBearerDefaults.AuthenticationScheme;


            })

            .AddJwtBearer(o =>
            {
                o.IncludeErrorDetails = true;
                o.TokenValidationParameters  = tokenValidationParameters;
                o.Events = new JwtBearerEvents()
                {
                    OnAuthenticationFailed = c =>
                    {
                        c.NoResult();

                        c.Response.StatusCode = 401;
                        c.Response.ContentType = "text/plain";

                        return c.Response.WriteAsync(c.Exception.ToString());
                    }

                };
            });

            return services;
        }
    }
}

どのように解決するのですか?

ここでは、コントローラを使った完全動作の最小限のサンプルを示します。PostmanやJavaScriptの呼び出しで確認していただければと思います。

  1. appsettings.json、appsettings.Development.json。セクションを追加します。なお、Keyはやや長め、Issuerはサービスのアドレスにします。

    ...
    ,"Tokens": {
        "Key": "Rather_very_long_key",
        "Issuer": "http://localhost:56268/"
    }
    ...
    
    

    !!! 実際のプロジェクトでは、キーを appsettings.json ファイルに保存しないでください。環境変数に保存して、このように受け取ります。

    Environment.GetEnvironmentVariable("JWT_KEY");
    
    

アップデイト : .net coreの設定がどのように動作するか見てみると、環境から正確に取得する必要はないようです。設定を使ってください。しかし、その代わりに、私たちはこの変数を環境変数に書き込むかもしれません、そして、私たちのコードは、設定の代わりに環境変数を好むでしょう。

  1. AuthRequest.cs : ログインとパスワードを渡すための値を保持するDtoです。

    public class AuthRequest
    {
        public string UserName { get; set; }
        public string Password { get; set; }
    }
    
    
  2. Startup.cs の Configure() メソッドの BEFORE app.UseMvc() :

    app.UseAuthentication();
    
    
  3. Startup.csのConfigureServices()内:

    services.AddAuthentication()
        .AddJwtBearer(cfg =>
        {
            cfg.RequireHttpsMetadata = false;
            cfg.SaveToken = true;
    
            cfg.TokenValidationParameters = new TokenValidationParameters()
            {
                ValidIssuer = Configuration["Tokens:Issuer"],
                ValidAudience = Configuration["Tokens:Issuer"],
                IssuerSigningKey = new SymmetricSecurityKey(Encoding.UTF8.GetBytes(Configuration["Tokens:Key"]))
            };
    
        });
    
    
  4. コントローラを追加します。

        [Route("api/[controller]")]
        public class TokenController : Controller
        {
            private readonly IConfiguration _config;
            private readonly IUserManager _userManager;
    
            public TokenController(IConfiguration configuration, IUserManager userManager)
            {
                _config = configuration;
                _userManager = userManager;
            }
    
            [HttpPost("")]
            [AllowAnonymous]
            public IActionResult Login([FromBody] AuthRequest authUserRequest)
            {
                var user = _userManager.FindByEmail(model.UserName);
    
                if (user != null)
                {
                    var checkPwd = _signInManager.CheckPasswordSignIn(user, model.authUserRequest);
                    if (checkPwd)
                    {
                        var claims = new[]
                        {
                            new Claim(JwtRegisteredClaimNames.Sub, user.UserName),
                            new Claim(JwtRegisteredClaimNames.Jti, user.Id.ToString()),
                        };
    
                        var key = new SymmetricSecurityKey(Encoding.UTF8.GetBytes(_config["Tokens:Key"]));
                        var creds = new SigningCredentials(key, SecurityAlgorithms.HmacSha256);
    
                        var token = new JwtSecurityToken(_config["Tokens:Issuer"],
                        _config["Tokens:Issuer"],
                        claims,
                        expires: DateTime.Now.AddMinutes(30),
                        signingCredentials: creds);
    
                        return Ok(new { token = new JwtSecurityTokenHandler().WriteToken(token) });
                    }
                }
    
                return BadRequest("Could not create token");
            }}
    
    

以上です。乾杯!

UPDATEです。 現在のユーザーを取得する方法について質問されました。Todoです。

  1. Startup.cs の ConfigureServices() に以下を追加します。

    services.AddSingleton<IHttpContextAccessor, HttpContextAccessor>();
    
    
  2. コントローラでコンストラクタに追加します。

    private readonly int _currentUser;
    public MyController(IHttpContextAccessor httpContextAccessor)
    {
       _currentUser = httpContextAccessor.CurrentUser();
    }
    
    
  3. どこかに拡張機能を追加し、Controllerで使用します(...を使用します)。

    public static class IHttpContextAccessorExtension
    {
        public static int CurrentUser(this IHttpContextAccessor httpContextAccessor)
        {
            var stringId = httpContextAccessor?.HttpContext?.User?.FindFirst(JwtRegisteredClaimNames.Jti)?.Value;
            int.TryParse(stringId ?? "0", out int userId);
    
            return userId;
        }
    }