【问题标题】:ASP.NET Core Web API custom AuthorizeAttribute issueASP.NET Core Web API 自定义 AuthorizeAttribute 问题
【发布时间】:2021-03-09 07:28:05
【问题描述】:

我正在开发 ASP.NET Core Web API。我正在尝试创建一个自定义 Authorize 属性,但我被卡住了。我无法理解我错过了什么。我对Authorize 属性和过滤器有以下代码:

public class AuthorizeAttribute : TypeFilterAttribute
{
    public AuthorizeAttribute(params string[] claim) : base(typeof(AuthorizeFilter))
    {
        Arguments = new object[] { claim };
    }
}

public class AuthorizeFilter : IAuthorizationFilter
{
    readonly string[] _claim;

    public AuthorizeFilter(params string[] claim)
    {
        _claim = claim;
    }

    public void OnAuthorization(AuthorizationFilterContext context)
    {
        var IsAuthenticated = context.HttpContext.User.Identity.IsAuthenticated;
        var claimsIndentity = context.HttpContext.User.Identity as ClaimsIdentity;

        if (IsAuthenticated)
        {
            bool flagClaim = false;
            foreach (var item in _claim)
            {
                if (context.HttpContext.User.HasClaim("Role", item))
                    flagClaim = true;
            }

            if (!flagClaim)
            {
                //if (context.HttpContext.Request.IsAjaxRequest())
                    context.HttpContext.Response.StatusCode = (int)HttpStatusCode.Forbidden; //Set HTTP 403 
                //else
                //    context.Result = new RedirectResult("~/Login/Index");
            }
        }
        else
        {
            //if (context.HttpContext.Request.IsAjaxRequest())
            //{
                context.HttpContext.Response.StatusCode = (int)HttpStatusCode.Unauthorized; //Set HTTP 401 -   
            //}
            //else
            //{
            //    context.Result = new RedirectResult("~/Login/Index");
            //}
        }
        return;
    }
}

我从某个地方复制了这段代码并注释了不必要的行。

这是我的控制器类,我试图在其中放置:

[Route("api/[controller]/[action]")]
[ApiController]
[Authorize]
public class JobController : ControllerBase
{
    // GET: api/<JobController>
    [HttpGet]
    [ActionName("GetAll")]
    public List<Job> Get()
    {
        return JobDataLog.GetAllJobQueue();
    }

    // GET api/<JobController>/5
    [HttpGet("{ID}")]
    [ActionName("GetByID")]
    public Job Get(Guid ID)
    {
        return JobDataLog.GetJob(ID);
    }

    // GET api/<JobController>/5
    [HttpGet]
    [ActionName("GetCount")]
    public int GetCount()
    {
        return JobDataLog.GetJobTotal();
    }
}

还有 Startup.cs 的 Configure 和 ConfigureService 方法

// This method gets called by the runtime. Use this method to add services to the container.
        public void ConfigureServices(IServiceCollection services)
        {
            services.AddControllers();
            services.AddDistributedMemoryCache();
            services.AddSession(options =>
            {
                options.IdleTimeout = TimeSpan.FromMinutes(60);
            });

            var tokenKey = Configuration.GetValue<string>("TokenKey");
            var key = Encoding.ASCII.GetBytes(tokenKey);
            services.AddAuthentication(x => { x.DefaultAuthenticateScheme = JwtBearerDefaults.AuthenticationScheme; })
                .AddJwtBearer(x =>
            {
                x.RequireHttpsMetadata = false;
                x.SaveToken = true;
                x.TokenValidationParameters = new TokenValidationParameters
                {
                    ValidateIssuerSigningKey = true,
                    IssuerSigningKey = new SymmetricSecurityKey(key),
                    ValidateIssuer = false,
                    ValidateAudience = false
                };
            });
            services.AddSingleton<IJWTAuthenticationManager>(new JWTAuthenticationManager(tokenKey));
        }

        // 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.UseCookiePolicy();
            app.UseSession();
            app.Use(async (context, next) =>
            {
                var JWToken = context.Session.GetString("JWToken");
                if (!string.IsNullOrEmpty(JWToken))
                {
                    context.Request.Headers.Add("Authorization", "Bearer " + JWToken);
                }
                await next();
            });

            app.UseAuthentication();
            app.UseAuthorization();

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

问题是即使这个控制器有Authorize 属性,所有的动作都被调用,即使Authorize 过滤器使授权无效。

当我将以下代码放在OnAuthorization 方法中时:

context.Result = new StatusCodeResult(StatusCodes.Status401Unauthorized);

它阻止了所有操作的访问,包括那些具有AllowAnnoynmous 属性的操作。

请帮帮我,我在过去 3 个小时里一直卡在这个问题上。

【问题讨论】:

  • 您是否引用了正确的[Authorize] 属性?重命名并尝试:例如MyAuthorizeAttribute
  • [Authorize] 指的是正确的,我没有在文件中添加 Microsoft.AspNetCore.Authorization 引用。另外我刚刚重命名并检查过,它显示语法错误,这意味着引用是正确的
  • 好奇你为什么要绕过提供的Authorize 属性?我会调查 IAuthorizationRequirementAuthorizationHandler&lt;T&gt; 如果它不能做你想要的开箱即用。

标签: c# asp.net-core asp.net-core-webapi


【解决方案1】:

如果您真的想使用自定义 AuthorizeAttribute,那么就可以了,这很有效。 :)

你会有一些波浪线,但 VS 将能够自动添加 using 语句。

原代码有多个问题:

  1. 设置 Reponse.StatusCode 实际上不会导致返回响应。
  2. HttpContext.User 首先不会被填充,因为 ASP.NET Core 仅在使用内置 AuthorizeAttribute 保护端点时才尝试对用户进行身份验证并填充用户的声明/身份。以下代码通过派生AuthorizeAttribute 解决了这个问题。
  3. 在这种情况下,不需要额外的过滤器工厂类,因为您没有注入依赖项。但是,如果您必须注入,我认为您会很不走运,因为您无法同时从 TypeFilterAttributeAuthorizeAttribute 派生,并且声明列表将始终为空。

工作代码

public class MyAuthorizeAttribute : AuthorizeAttribute, IAuthorizationFilter
{
    readonly string[] _requiredClaims;

    public MyAuthorizeAttribute(params string[] claims)
    {
        _requiredClaims = claims;
    }

    public void OnAuthorization(AuthorizationFilterContext context)
    {
        var isAuthenticated = context.HttpContext.User.Identity.IsAuthenticated;
        if (!isAuthenticated)
        {
            context.Result = new UnauthorizedResult();
            return;
        }

        var hasAllRequredClaims = _requiredClaims.All(claim => context.HttpContext.User.HasClaim(x => x.Type == claim));
        if (!hasAllRequredClaims)
        {
            context.Result = new ForbidResult();
            return;
        }
    }
}

您或许应该改用政策

之所以如此糟糕,是因为 ASP.NET Core 团队不希望您编写自定义授权属性。见this answer on the subject。 “正确”的方法是创建策略,并将您的索赔要求分配给这些策略。但我也觉得授权这么不灵活,缺乏对基本场景的支持是很愚蠢的。

【讨论】:

  • “您可能应该改用政策” - 是的 +1。
猜你喜欢
  • 2012-03-17
  • 2019-08-06
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2012-09-19
  • 2015-10-06
相关资源
最近更新 更多