【问题标题】:Web Api Bearer JWT Token Authentication with Api Key fails on successive calls after successful token authenticationWeb Api Bearer JWT Token Authentication with Api Key 在成功令牌认证后的连续调用中失败
【发布时间】:2018-04-20 11:25:13
【问题描述】:

我创建了一个 MVC Core API,它使用 api 密钥对用户进行身份验证。成功验证后,它会发回一个 JWT 令牌,用于任何后续请求。 我可以使用有效的 api 密钥成功进行身份验证,并获得一个令牌作为响应。然后我可以使用这个令牌发出请求,但下一个请求失败。 在我的真实应用程序中,消费者是一个 MVC Core 站点,直到现在我还没有注意到这个问题,因为在每个 mvc 控制器操作中我都在调用一个 api 操作,但现在我需要一个接一个地调用两个 api 操作第二个相同的 mvc 动作失败了,我不明白为什么。

我已在示例 Web api 和控制台应用程序中重现了我的问题。

这是 MVC Core 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; }
    }
}

关于为什么第二次调用失败的任何帮助?

web api 输出窗口中显示的错误信息是:

承载未通过身份验证。失败消息:无SecurityTokenValidator可用的令牌:eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJqdGkiOiIyNmFiNjQzYjFjOTM0MzYwYjI4NDAxMzZjNDIxOTBlZSIsImlhdCI6MTUxMDA2NDg0MywiaHR0cDovL3NjaGVtYXMueG1sc29hcC5vcmcvd3MvMjAwNS8wNS9pZGVudGl0eS9jbGFpbXMvbmFtZWlkZW50aWZpZXIiOiIxIiwiR2xvYmFsSWQiOiI2NjVjYWEzYjYxYmY0MWRmOGIzMTVhODY5YzQzMmJkYyIsImh0dHA6Ly9zY2hlbWFzLm1pY3Jvc29mdC5jb20vd3MvMjAwOC8wNi9pZGVudGl0eS9jbGFpbXMvcm9sZSI6IkFkbWluaXN0cmF0b3IiLCJuYmYiOjE1MTAwNjQ4NDMsImV4cCI6MTUxMDA2NjY0MywiaXNzIjoiaHR0cDovL2xvY2FsaG9zdDo0NDM2MCIsImF1ZCI6Imh0dHA6Ly9sb2NhbGhvc3Q6NDQzNjAifQ.wJ86Ut2dmbDRDCNXU2kWXeQ1pQGkiVtUx7oSyJIZMzc P>

【问题讨论】:

    标签: asp.net-core asp.net-core-mvc


    【解决方案1】:

    它不起作用,因为这段代码TryAddWithoutValidation("Authorization", $"Bearer {token}"); 将令牌添加到授权标头中已经存在的内容之上,而没有先清除它。因此,连续调用会在已经包含不记名令牌的标头中添加带有令牌的不记名字符串。

    【讨论】:

      猜你喜欢
      • 2019-05-01
      • 1970-01-01
      • 2016-10-10
      • 2021-08-02
      • 2018-11-20
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2021-12-08
      相关资源
      最近更新 更多