ASP.NET Core API 使用 JWT 不记名令牌进行身份验证

Posted

技术标签:

【中文标题】ASP.NET Core API 使用 JWT 不记名令牌进行身份验证【英文标题】:ASP.NET Core API authenticate using JWT bearer tokens 【发布时间】:2021-04-29 06:54:50 【问题描述】:

我有一个新的 API,我想使用外部安全提供程序(身份验证服务器)进行身份验证。我有以下代码,但是在设置完所有内容后调用经过身份验证的操作时出现错误。

Startup.cs:

public async void ConfigureServices(IServiceCollection services)
        
            services.AddControllers();

            services.AddScoped<ITemperatureLoggerRepository<ConsolidatedTables>, TemperatureLoggerRepository>();

            services.AddDbContext<TemperatureLoggerContext>(options => options.UseSqlServer(Configuration["Data:ConnectionString:TemperatureLoggerDB"]));

            services.AddSwaggerGen(swagger =>
            
                swagger.SwaggerDoc("v1", new OpenApiInfo  Title = "Temperature Logger API", Version = "Version 1" );
                swagger.AddSecurityDefinition("Bearer", new OpenApiSecurityScheme()
                
                    Name = "Authorization",
                    Type = SecuritySchemeType.ApiKey,
                    Scheme = "Bearer",
                    BearerFormat = "JWT",
                    In = ParameterLocation.Header,
                    Description = "JWT Authorization header using the Bearer scheme. \r\n\r\n Enter 'Bearer' [space] and then your token in the text input below.\r\n\r\nExample: \"Bearer 12345abcdef\"",
                );
                swagger.AddSecurityRequirement(new OpenApiSecurityRequirement
                
                    
                          new OpenApiSecurityScheme
                            
                                Reference = new OpenApiReference
                                
                                    Type = ReferenceType.SecurityScheme,
                                    Id = "Bearer"
                                
                            ,
                            new string[] 
                    
                );
            );

            Response<List<Client>> response = await services.AddAuthServer(Configuration);

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

            ).AddJwtBearer(options =>
            
                var signingKeys = new List<SymmetricSecurityKey>();
                foreach (var client in response.Data)
                
                    signingKeys.Add(new SymmetricSecurityKey(Encoding.UTF8.GetBytes(client.Base64Secret)));
                

                options.TokenValidationParameters = new TokenValidationParameters
                
                    ValidateIssuer = true,
                    ValidateAudience = true,
                    ValidateLifetime = false,
                    ValidateIssuerSigningKey = true,
                    ValidIssuer = Configuration["Jwt:Issuer"],
                    ValidAudience = Configuration["Jwt:Issuer"],
                    IssuerSigningKeys = signingKeys
                ;
            ).AddOAuthValidation();

        

        // This method gets called by the runtime. Use this method to configure the HTTP request pipeline.
        public void Configure(IApplicationBuilder app, IWebHostEnvironment env)
        
            if (env.IsDevelopment())
            
                app.UseDeveloperExceptionPage();
            

            app.UseHttpsRedirection();

            app.UseRouting();

            app.UseAuthorization();

            app.UseEndpoints(endpoints =>
            
                endpoints.MapControllers();
            );

            app.UseAuthentication();
            
            app.UseSwagger();

            app.UseSwaggerUI(c =>
            
                c.SwaggerEndpoint("/swagger/v1/swagger.json", "Temperature Logger API");
            );
        

调用允许访问 API 的客户端列表的 AuthServer.cs:

public static async Task<Response<List<Client>>> AddAuthServer(this IServiceCollection collection, IConfiguration config)
        
            var clientHandler = new HttpClientHandler
            
                CookieContainer = new CookieContainer(),
                UseCookies = true
            ;

            using (var httpClient = new HttpClient(clientHandler))
            
                HttpContent content = new FormUrlEncodedContent(new[]
                
                    new KeyValuePair<string, string>("grant_type", "client_credentials"),
                    new KeyValuePair<string, string>("client_id", config["Jwt:ClientId"]),
                    new KeyValuePair<string, string>("client_secret", config["Jwt:ClientSecret"])
                );
                httpClient.BaseAddress = new Uri(config["Jwt:Issuer"]);
                var responseMessage = await httpClient.PostAsync(config["Jwt:Issuer"] + "oauth2/token", content);
                var result = await responseMessage.Content.ReadAsStringAsync();
                _tokenResponse = JsonConvert.DeserializeObject<Token>(result);
            

            var clientHandler2 = new HttpClientHandler
            
                CookieContainer = new CookieContainer(),
                UseCookies = true
            ;
            using (var httpClient2 = new HttpClient(clientHandler2))
            
                httpClient2.DefaultRequestHeaders.Authorization = new AuthenticationHeaderValue("Bearer", _tokenResponse.access_token);
                using (var response = await httpClient2.GetAsync(config["Jwt:Issuer"] + "api/Client/GetIdKeys"))
                
                    if (!response.IsSuccessStatusCode) throw new Exception(response.StatusCode.ToString());
                    //string clientsResponse = await response.Content.ReadAsStringAsync();
                    return await response.Content.ReadAsAsync<Response<List<Client>>>();
                
            
        

HomeController.cs(经过身份验证的操作):

[Produces("application/json")]
    [Route("api/home")]
    [ApiController]
    [Authorize]
    public class HomeController : ControllerBase
    
        [HttpGet]
        [Authorize(AuthenticationSchemes = JwtBearerDefaults.AuthenticationScheme)]
        public IActionResult Index()
        
            return Ok("Welcome to our Protected World!");
        
    

结果:

请协助我如何使其正常工作。 API 还分配有其 on ClientId 和 ClientSecret。

【问题讨论】:

您可以尝试将app.UseAuthentication(); 移到app.UseAuthorization(); 之前吗? 感谢您的回复,当我这样做时,现在我收到以下错误: System.InvalidOperationException: Endpoint HomeController.Index '' 包含授权元数据,但未找到支持授权的中间件。 我不确定,但认为您需要致电services.AddAuthorization(); 还是不行,我尝试了这个建议。 【参考方案1】:

我设法找出了问题所在,而且很简单,但我错过了。这实际上与获取我的观众列表或任何额外配置无关。我需要解码从商店获得的密钥:

var signingKeys = new List<SymmetricSecurityKey>();
foreach (var client in response.Data)

   signingKeys.Add(new SymmetricSecurityKey(Base64UrlEncoder.DecodeBytes(/***base64Secret***/)));

【讨论】:

以上是关于ASP.NET Core API 使用 JWT 不记名令牌进行身份验证的主要内容,如果未能解决你的问题,请参考以下文章

将 JWT Bearer Authentication Web API 与 Asp.Net Core 2.0 结合使用的问题

ASP.NET Core Web Api之JWT

ASP.Net Core - 使用 WebAPI 和 MVC 前端的 JWT 身份验证不起作用

ASP.NET Core JWT 和声明

asp.net core 2.0 web api基于JWT自定义策略授权

ASP.NET Core Web Api之JWT刷新Token