.Net Core JWT 身份验证与自定义 API 密钥中间件

Posted

技术标签:

【中文标题】.Net Core JWT 身份验证与自定义 API 密钥中间件【英文标题】:.Net Core JWT Authentication with custom API Key Middleware 【发布时间】:2018-02-27 21:10:43 【问题描述】:

我有一个使用 JWT 令牌授权用户的 .Net Core 2.0 应用程序。 这一切都很好,但我希望有某种 API 密钥机制来允许其他应用程序集成,但我似乎无法让它与当前的身份验证一起使用。

代码:

Startup.cs

public void Configure(IApplicationBuilder app, IHostingEnvironment env, ILoggerFactory loggerFactory,
            IMemoryCache cache, IServiceProvider serviceProvider)

    app.UseAuthentication();

    ApiKeyMiddlewear(app, serviceProvider);

    app.UseMvc(routes =>
     
        routes.MapRoute(
               name: "default",
                template: "controller=Home/action=Index/id?");

            routes.MapSpaFallbackRoute(
                name: "spa-fallback",
                defaults: new  controller = "Home", action = "Index" );
        );
    



    private static void ApiKeyMiddlewear(IApplicationBuilder app, IServiceProvider serviceProvider)
    
        app.Use(async (context, next) =>
        
            if (context.Request.Path.StartsWithSegments(new PathString("/api")))
            
                // Let's check if this is an API Call
                if (context.Request.Headers["ApiKey"].Any())
                
                    // validate the supplied API key
                    // Validate it
                    var headerKey = context.Request.Headers["ApiKey"].FirstOrDefault();
                    var settingsProvider = serviceProvider.GetService<ISettingsService<OmbiSettings>>();
                    var ombiSettings = settingsProvider.GetSettings();
                    var valid = ombiSettings.ApiKey.Equals(headerKey, StringComparison.CurrentCultureIgnoreCase);
                    if (!valid)
                    
                        context.Response.StatusCode = (int) HttpStatusCode.Unauthorized;
                        await context.Response.WriteAsync("Invalid API Key");
                    
                    else
                    
                        var identity = new GenericIdentity("API");
                        identity.AddClaim(new System.Security.Claims.Claim("Origin", "Api"));
                        identity.AddClaim(new System.Security.Claims.Claim("role", "Admin"));

                        var principal = new GenericPrincipal(identity, new[] "ApiUser");
                        context.User = principal;
                        await next();
                    
                
                else
                
                    await next();
                
            
            else
            
                await next();
            
        );
    

所以在上面的代码中,您可以看到我正在拦截提供名为 ApiKey 的标头的 HTTP 请求,然后将其验证为我存储的内容。这部分一切正常,但是当使用 Authorize 属性调用 API 方法时,这不起作用,我收到以下错误日志:

2017-09-19 08:15:17.280 +01:00 [Information] Request starting HTTP/1.1 POST http://localhost:52038/api/v1/Identity/ application/json 372
2017-09-19 08:15:21.967 +01:00 [Information] Authorization failed for user: "API".
2017-09-19 08:15:21.976 +01:00 [Information] Authorization failed for the request at filter '"Microsoft.AspNetCore.Mvc.Authorization.AuthorizeFilter"'.
2017-09-19 08:15:21.981 +01:00 [Information] Executing ForbidResult with authentication schemes ([]).
2017-09-19 08:15:21.991 +01:00 [Information] AuthenticationScheme: "Bearer" was forbidden.
2017-09-19 08:15:21.996 +01:00 [Information] Executed action "Ombi.Controllers.IdentityController.CreateUser (Ombi)" in 38.8268ms
2017-09-19 08:15:22.004 +01:00 [Information] Request finished in 4723.032ms 403 

现在我猜这与仅提供 ApiKey 标头而不是带有正确 JWT 令牌的 Authorization 标头的请求有关。

我怎样才能只提供 ApiKey 标头,当没有 ApiKey 标头时,然后回退到需要 JWT 令牌?

【问题讨论】:

你在使用 Asp.net Identity 吗? @AramKocharyan 是的 您是否将Authorize 属性与Roles 属性一起使用?喜欢[Authorize(Roles = “foo”)] Claim("role", "Admin") 应用到GenericPrincipal 不会影响任何事情,因为GenericPrincipal 与Claims 无关。所以如果你想申请管理员角色,你需要在构造函数参数中添加它:new GenericPrincipal(identity, new[] "Admin", "ApiUser"); @AramKocharyan 这行得通!如果您不介意回答,我可以给您赏金? 【参考方案1】:

Claim("role", "Admin") 应用到GenericPrincipal 不会有任何影响,因为GenericPrincipal 与角色声明无关。所以如果你想为GenericPrincipal申请管理员角色,你需要在构造函数参数中添加它:

 var principal = new GenericPrincipal(identity, new[] "Admin","ApiUser");
 context.User = principal;

【讨论】:

以上是关于.Net Core JWT 身份验证与自定义 API 密钥中间件的主要内容,如果未能解决你的问题,请参考以下文章

asp.net core 2.0 授权在身份验证(JWT)之前触发

ASP.NET Core 2.2 JWT 身份验证

.Net Core 自定义身份验证使用 API 密钥和 Identity Server 4

如何在 .NET Core 中为 JWT 令牌设计自定义验证器?

ASP.NET Core 中的 Jwt 令牌身份验证

asp.Net core 2.2中的多种身份验证方法