2017-11-07 23 views
0

APIキーを使用してユーザーを認証するMVCコアAPIを作成しました。認証に成功すると、後続の要求に使用するJWTトークンを返します。 有効なapiキーで正常に認証され、返信としてトークンを取得できます。次に、このトークンを使用してリクエストを行うことはできますが、次のリクエストは失敗します。 私の実際のアプリケーションでは、コンシューマはMVCコアサイトです。これまでMVCコントローラのアクションごとに1つのAPIアクションが呼び出されていましたが、今度は2つのAPIアクションを1つずつ呼び出す必要があるため、同じMVCアクションで2つ目は失敗し、なぜ私は理解できません。Web Api Bearer JWT Apiキーを使用したトークン認証は、トークン認証が成功した後の連続呼び出しで失敗します。

サンプルWeb APIおよびコンソールアプリケーションで問題を再現しました。

これはMVCのコアAPIエンドポイントは、APIキーを検証してJWTトークンを生成するためのコードです:

using System; 
using System.Collections.Generic; 
using System.IdentityModel.Tokens.Jwt; 
using System.Security.Claims; 
using System.Text; 
using System.Threading.Tasks; 
using Microsoft.AspNetCore.Authorization; 
using Microsoft.AspNetCore.Mvc; 
using Microsoft.IdentityModel.Tokens; 
using PureHub.Services.Models.Valid8.Authentication; 

namespace BugApiJwt.Controllers 
{ 
    [Authorize] 
    [Route("v1/[controller]")] 
    public class AuthenticationController : ControllerBase 
    { 
     [AllowAnonymous] 
     [HttpPost("[action]")] 
     public virtual async Task<IActionResult> Token([FromBody] ApiLoginRequest model) 
     { 
      if (model != null) 
      { 
       if (model.ApiKey == "VdPfwrL+mpRHKgzAIm9js7e/J9AbJshoPgv1nIZiat22R") 
       { 
        var claims = new List<Claim> 
        { 
         new Claim(JwtRegisteredClaimNames.Jti, Guid.NewGuid().ToString("N")), 
         new Claim(JwtRegisteredClaimNames.Iat, 
          new DateTimeOffset(DateTime.UtcNow).ToUniversalTime().ToUnixTimeSeconds().ToString(), 
          ClaimValueTypes.Integer64) 
        }; 

        var key = new SymmetricSecurityKey(Encoding.UTF8.GetBytes("FTTaIMmkh3awD/4JF0iHgAfNiB6/C/gFeDdrKU/4YG1ZK36o16Ja4wLO+1Qft6yd+heHPRB2uQqXd76p5bXXPQ==")); 
        var creds = new SigningCredentials(key, SecurityAlgorithms.HmacSha256); 

        var token = new JwtSecurityToken(
         issuer: "http://localhost:58393/", 
         audience: "http://localhost:58393/", 
         claims: claims, 
         expires: DateTime.UtcNow.AddMinutes(30), 
         signingCredentials: creds); 

        return Ok(new ApiLoginResponse 
        { 
         Token = new JwtSecurityTokenHandler().WriteToken(token), 
         Expiration = token.ValidTo 
        }); 
       } 
      } 

      return BadRequest(); 
     } 
    } 
} 

これは、保護されたリソースである:

using System.Collections.Generic; 
using Microsoft.AspNetCore.Authorization; 
using Microsoft.AspNetCore.Mvc; 

namespace BugApiJwt.Controllers 
{ 
    [Authorize] 
    [Route("v1/values")] 
    public class ValuesController : Controller 
    { 
     [HttpGet] 
     public IEnumerable<string> Get() 
     { 
      return new[] { "value1", "value2" }; 
     } 

     [HttpGet("{id}")] 
     public string Get(int id) 
     { 
      return $"You said: {id}"; 
     } 
    } 
} 

そして、これは私のスタートアップであります:

using System.Text; 
using Microsoft.AspNetCore.Authentication.JwtBearer; 
using Microsoft.AspNetCore.Builder; 
using Microsoft.AspNetCore.Hosting; 
using Microsoft.Extensions.Configuration; 
using Microsoft.Extensions.DependencyInjection; 
using Microsoft.IdentityModel.Tokens; 

namespace BugApiJwt 
{ 
    public class Startup 
    { 
     public Startup(IConfiguration configuration) 
     { 
      Configuration = configuration; 
     } 

     public IConfiguration Configuration { get; } 

     public void ConfigureServices(IServiceCollection services) 
     { 
      services.AddAuthentication(JwtBearerDefaults.AuthenticationScheme) 
       .AddJwtBearer(options => 
       { 
        options.RequireHttpsMetadata = false; 

        options.TokenValidationParameters = new TokenValidationParameters 
        { 
         ValidIssuer = "http://localhost:58393/", 
         ValidAudience = "http://localhost:58393/", 
         ValidateIssuerSigningKey = true, 
         IssuerSigningKey = new SymmetricSecurityKey(Encoding.UTF8.GetBytes("FTTaIMmkh3awD/4JF0iHgAfNiB6/C/gFeDdrKU/4YG1ZK36o16Ja4wLO+1Qft6yd+heHPRB2uQqXd76p5bXXPQ==")), 
        }; 
       }); 
      services.AddMvc(); 
     } 

     public void Configure(IApplicationBuilder app, IHostingEnvironment env) 
     { 
      if (env.IsDevelopment()) 
      { 
       app.UseDeveloperExceptionPage(); 
      } 
      app.UseAuthentication(); 
      app.UseMvc(); 
     } 
    } 
} 

これは私がテストしているコンソールアプリケーションです:

using System; 
using System.Net.Http; 
using System.Net.Http.Headers; 
using System.Text; 
using System.Threading.Tasks; 
using Newtonsoft.Json; 
using Newtonsoft.Json.Serialization; 

namespace BugApiJwt.Console 
{ 
    public class Program 
    { 
     private const string ApiKey = "VdPfwrL+mpRHKgzAIm9js7e/J9AbJshoPgv1nIZiat22R"; 
     private const string BaseAddress = "http://localhost:58393/"; 
     private static HttpClient _client = new HttpClient(); 
     private static string _realToken = string.Empty; 

     private static void Main() 
     { 
      _client = new HttpClient 
      { 
       BaseAddress = new Uri(BaseAddress) 
      }; 

      _client.DefaultRequestHeaders.Accept.Clear(); 
      _client.DefaultRequestHeaders.Accept.Add(new MediaTypeWithQualityHeaderValue("application/json")); 

      // Works 
      System.Console.WriteLine("Call GetOne"); 
      var getOne = Get().GetAwaiter().GetResult(); 
      System.Console.WriteLine(getOne); 

      // Fails 
      System.Console.WriteLine("Call GetAll"); 
      var getTwo = GetAll().GetAwaiter().GetResult(); 
      System.Console.WriteLine(getTwo); 

      System.Console.WriteLine("All Finished. Press Enter to exit"); 
      System.Console.ReadLine(); 
     } 

     private static async Task<string> GetAuthenticationToken() 
     { 
      const string resource = "v1/authentication/token"; 

      if (!string.IsNullOrEmpty(_realToken)){return _realToken;} 

      var loginRequest = new ApiLoginRequest{ApiKey = ApiKey}; 

      var httpResponseMessage = await _client.PostAsync(resource, ObjectToJsonContent(loginRequest)).ConfigureAwait(false); 

      if (httpResponseMessage.IsSuccessStatusCode) 
      { 
       var content = await httpResponseMessage.Content.ReadAsStringAsync(); 

       var obj = JsonConvert.DeserializeObject<ApiLoginResponse>(content); 

       _realToken = obj.Token; 

       return obj.Token; 
      } 

      throw new Exception("Token is null"); 
     } 
     public static async Task<string> Get() 
     { 
      var resource = "v1/values/1"; 
      var token = await GetAuthenticationToken(); 

      _client.DefaultRequestHeaders.TryAddWithoutValidation("Authorization", $"Bearer {token}"); 

      var httpResponseMessage = await _client.GetAsync(resource); 

      System.Console.WriteLine(httpResponseMessage.RequestMessage.Headers.Authorization); 
      System.Console.WriteLine(httpResponseMessage.Headers.WwwAuthenticate); 

      var content = await httpResponseMessage.Content.ReadAsStringAsync(); 

      return content; 
     } 
     public static async Task<string> GetAll() 
     { 
      var resource = "v1/values"; 
      var token = await GetAuthenticationToken(); 

      _client.DefaultRequestHeaders.TryAddWithoutValidation("Authorization", $"Bearer {token}"); 

      var httpResponseMessage = await _client.GetAsync(resource); 

      System.Console.WriteLine(httpResponseMessage.RequestMessage.Headers.Authorization); 
      System.Console.WriteLine(httpResponseMessage.Headers.WwwAuthenticate); 

      var content = await httpResponseMessage.Content.ReadAsStringAsync(); 

      return content; 
     } 
     private static StringContent ObjectToJsonContent<T>(T objectToPost) where T : class, new() 
     { 
      var tJson = JsonConvert.SerializeObject(objectToPost, 
       Formatting.Indented, new JsonSerializerSettings 
       { 
        NullValueHandling = NullValueHandling.Ignore, 
        ContractResolver = new CamelCasePropertyNamesContractResolver() 
       }); 

      return new StringContent(tJson, Encoding.UTF8, "application/json"); 
     } 

    } 
    public class ApiLoginRequest 
    { 
     public string ApiKey { get; set; } 
    } 
    public class ApiLoginResponse 
    { 
     public string Token { get; set; } 

     public DateTime Expiration { get; set; } 
    } 
} 

2番目の呼び出しが失敗する理由についてのヘルプはありますか?

ウェブAPI出力ウィンドウに表示されるエラーメッセージがある:

ベアラが認証されませんでした。失敗メッセージ:トークンのために利用可能なSecurityTokenValidator:eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJqdGkiOiIyNmFiNjQzYjFjOTM0MzYwYjI4NDAxMzZjNDIxOTBlZSIsImlhdCI6MTUxMDA2NDg0MywiaHR0cDovL3NjaGVtYXMueG1sc29hcC5vcmcvd3MvMjAwNS8wNS9pZGVudGl0eS9jbGFpbXMvbmFtZWlkZW50aWZpZXIiOiIxIiwiR2xvYmFsSWQiOiI2NjVjYWEzYjYxYmY0MWRmOGIzMTVhODY5YzQzMmJkYyIsImh0dHA6Ly9zY2hlbWFzLm1pY3Jvc29mdC5jb20vd3MvMjAwOC8wNi9pZGVudGl0eS9jbGFpbXMvcm9sZSI6IkFkbWluaXN0cmF0b3IiLCJuYmYiOjE1MTAwNjQ4NDMsImV4cCI6MTUxMDA2NjY0MywiaXNzIjoiaHR0cDovL2xvY2FsaG9zdDo0NDM2MCIsImF1ZCI6Imh0dHA6Ly9sb2NhbGhvc3Q6NDQzNjAifQ.wJ86Ut2dmbDRDCNXU2kWXeQ1pQGkiVtUx7oSyJIZMzc

答えて

0

コードTryAddWithoutValidation("Authorization", $"Bearer {token}");のこの作品は、それを最初にクリアせずに認証ヘッダーにすでにあるものの上にトークンを追加しますので、それは動作しません。その結果、連続する呼び出しは、既にベアラトークンを含むヘッダ内のトークンと共にベアラ文字列を追加する。

関連する問題