C# 如何在ASP Net Core中授权Web API控制器

C# 如何在ASP Net Core中授权Web API控制器,c#,asp.net,json,asp.net-core,authorize,C#,Asp.net,Json,Asp.net Core,Authorize,我已经为C#ASP网络内核中的后端创建了一个API。我试图找到一种授权路由的方法,这样它就可以在url(如“”)中接收API密钥来验证路由并以JSON格式显示数据 我知道在ASP NET Core identity中有一种对路由进行身份验证的方法,但这需要用户首先登录。如何使用API密钥保护我的API路由?您尝试执行的操作将无法保护web API。我建议您研究OAuth/OpenID。有一个名为Identity Server 4的开源.net核心实现 但是,要回答您的问题,您可以创建一个自定义属

我已经为C#ASP网络内核中的后端创建了一个API。我试图找到一种授权路由的方法,这样它就可以在url(如“”)中接收API密钥来验证路由并以JSON格式显示数据


我知道在ASP NET Core identity中有一种对路由进行身份验证的方法,但这需要用户首先登录。如何使用API密钥保护我的API路由?

您尝试执行的操作将无法保护web API。我建议您研究OAuth/OpenID。有一个名为Identity Server 4的开源.net核心实现


但是,要回答您的问题,您可以创建一个自定义属性来验证传递给您的操作的密钥,或者只需在每个操作中处理验证。在.NETCore中没有内置的方法来实现这一点,您必须手动处理api密钥,就像其他传递给web api的值一样

听上去,您试图实现的是一个替代身份验证系统和一个自定义授权系统,它使用这个
查询字符串参数(这可能不是最佳设计)

第一步是基于此
QueryString
参数对用户进行身份验证。现在最好的方法(IMO)是使用您自己的身份验证处理程序。查看的代码可以揭示一些现有身份验证系统的内部工作原理

// This method gets called by the runtime. Use this method to add services to the container.
public void ConfigureServices(IServiceCollection services)
{
    services.AddAuthentication(); //adds the auth services
    services.AddMvc();
}

// This method gets called by the runtime. Use this method to configure the HTTP request pipeline.
public void Configure(IApplicationBuilder app, IHostingEnvironment env, ILoggerFactory loggerFactory)
{
    loggerFactory.AddConsole(Configuration.GetSection("Logging"));
    loggerFactory.AddDebug();

    app.UseQueryStringAuthentication(); //add our query string auth

    //add mvc last
    app.UseMvc();
}
实际上,我们要做的是尽早拦截请求,验证该
密钥的存在,然后对请求进行身份验证

下面展示了这个基本系统

public class QueryStringAuthOptions : AuthenticationOptions
{
    public const string QueryStringAuthSchema = "QueryStringAuth";
    public const string QueryStringAuthClaim = "QueryStringKey";

    public QueryStringAuthOptions()
    {
        AuthenticationScheme = QueryStringAuthSchema;
    }

    public string QueryStringKeyParam { get; set; } = "key";

    public string ClaimsTypeName { get; set; } = "QueryStringKey";

    public AuthenticationProperties AuthenticationProperties { get; set; } = new AuthenticationProperties();
}

public class QueryStringAuthHandler : AuthenticationHandler<QueryStringAuthOptions>
{
    /// <summary>
    /// Handle authenticate async
    /// </summary>
    /// <returns></returns>
    protected override Task<AuthenticateResult> HandleAuthenticateAsync()
    {
        if (Request.Query.TryGetValue(Options.QueryStringKeyParam, out StringValues value) && value.Count > 0)
        {
            var key = value[0];

            //..do your authentication...

            if (!string.IsNullOrWhiteSpace(key))
            {
                //setup you claim
                var claimsPrinciple = new ClaimsPrincipal();
                claimsPrinciple.AddIdentity(new ClaimsIdentity(new[] { new Claim(Options.ClaimsTypeName, key) }, Options.AuthenticationScheme));

                //create the result ticket
                var ticket = new AuthenticationTicket(claimsPrinciple, Options.AuthenticationProperties, Options.AuthenticationScheme);
                var result = AuthenticateResult.Success(ticket);
                return Task.FromResult(result);
            }
        }
        return Task.FromResult(AuthenticateResult.Fail("Key not found or not valid"));

    }
}
这是非常基本的,但只是创建一个新的
QueryStringAuthHandler()
来处理身份验证请求。(我们之前创建的)。现在我们需要将这个中间件引入到管道中。因此,按照.Net约定,静态扩展类可以通过管理选项来实现这一点

public static class QueryStringAuthMiddlewareExtensions
{
    public static IApplicationBuilder UseQueryStringAuthentication(this IApplicationBuilder appBuilder)
    {
        if (appBuilder == null)
            throw new ArgumentNullException(nameof(appBuilder));

        var options = new QueryStringAuthOptions();
        return appBuilder.UseQueryStringAuthentication(options);
    }

    public static IApplicationBuilder UseQueryStringAuthentication(this IApplicationBuilder appBuilder, Action<QueryStringAuthOptions> optionsAction)
    {
        if (appBuilder == null)
            throw new ArgumentNullException(nameof(appBuilder));

        var options = new QueryStringAuthOptions();
        optionsAction?.Invoke(options);
        return appBuilder.UseQueryStringAuthentication(options);
    }

    public static IApplicationBuilder UseQueryStringAuthentication(this IApplicationBuilder appBuilder, QueryStringAuthOptions options)
    {
        if (appBuilder == null)
            throw new ArgumentNullException(nameof(appBuilder));

        if (options == null)
            throw new ArgumentNullException(nameof(options));

        return appBuilder.UseMiddleware<QueryStringAuthMiddleware>(Options.Create(options));
    }
}
我们就快到了,到目前为止,我们已经有了验证请求的机制,最好是创建声明(可以扩展),以便在需要时保存更多信息。最后一步是授权请求。这很简单,我们所需要做的就是告诉默认授权处理程序您正在使用的登录模式,此外,我们还需要我们前面应用的声明。回到
startup.cs中的
ConfigureServices
方法中,我们只需通过一些设置添加授权

public void ConfigureServices(IServiceCollection services)
{
    services.AddAuthorization(o =>
    {
        //override the default policy
        o.DefaultPolicy =  new AuthorizationPolicy(new[] { new ClaimsAuthorizationRequirement(QueryStringAuthOptions.QueryStringAuthClaim, new string[0]) }, new[] { QueryStringAuthOptions.QueryStringAuthSchema });

        //or add a policy
        //o.AddPolicy("QueryKeyPolicy", options =>
        //{
        //    options.RequireClaim(QueryStringAuthOptions.QueryStringAuthClaim);
        //    options.AddAuthenticationSchemes(QueryStringAuthOptions.QueryStringAuthSchema);
        //});

    });
    services.AddAuthentication(o =>
    {
        o.SignInScheme = QueryStringAuthOptions.QueryStringAuthSchema;
    }); //adds the auth services
    services.AddMvc();
}
在上面的代码片段中,我们有两个选项

  • 覆盖
    DefaultPolicy
  • 将新的
    策略
    添加到授权系统
  • 现在,您使用哪个选项取决于您自己。使用后一个选项需要明确告诉
    授权
    处理程序要使用哪个
    授权策略

    我建议你阅读以了解这些是如何工作的


    要使用此授权系统(取决于上面的选项),只需使用
    AuthorizeAttribute()
    (如果使用第二个选项,则使用策略名称)装饰控制器即可。

    这看起来非常有希望,我一定会尝试一下。为什么这不是一个好的设计?我遇到过许多API,它们基于用户使用键来验证url。@RushB你是对的,有很多(或曾经是)应用程序将API键嵌入url。令牌身份验证(如JWT等)提供了一种更安全的数据交换方式,这一点已经发生了很大的变化。通常,这在标题中使用,而不是在查询字符串中使用。现在,通过将其放置在URL中,这是一个公共场所(是的,SSL可能会隐藏),但可以公开分发。如果使用授权标头,则可以确保不会意外复制和粘贴此信息。@RushB刚刚偶然发现了此线程,这增加了一些要点。我知道这是一种不好的做法,您不希望密钥在url中公开,但如果我只是通过来自iOS应用程序的网络请求调用url,并且密钥将隐藏在我的代码中,该怎么办。通过Alamofire的网络请求是否仍然会留下url线索或某种线索?我也读了你发布的链接。我是否可以使用核心标识来授权路由,并在标头中提供用户名和密码凭据以使路由有效?我已经尝试过使用它,每次都会转到登录页面,而不是让我直接访问数据。
    public void ConfigureServices(IServiceCollection services)
    {
        services.AddAuthorization(o =>
        {
            //override the default policy
            o.DefaultPolicy =  new AuthorizationPolicy(new[] { new ClaimsAuthorizationRequirement(QueryStringAuthOptions.QueryStringAuthClaim, new string[0]) }, new[] { QueryStringAuthOptions.QueryStringAuthSchema });
    
            //or add a policy
            //o.AddPolicy("QueryKeyPolicy", options =>
            //{
            //    options.RequireClaim(QueryStringAuthOptions.QueryStringAuthClaim);
            //    options.AddAuthenticationSchemes(QueryStringAuthOptions.QueryStringAuthSchema);
            //});
    
        });
        services.AddAuthentication(o =>
        {
            o.SignInScheme = QueryStringAuthOptions.QueryStringAuthSchema;
        }); //adds the auth services
        services.AddMvc();
    }