zoukankan      html  css  js  c++  java
  • asp.net core3.1策略授权问题

    问题一:

    core2.2升级到3.1之后 2.2中策略授权使用context.Resource 在3.1版本中不再是AuthorizationFilterContext类型,而是Endpoint类型。不能再通过context.Resource来获取http请求头相关的数据。下边的代码在core3.1中已经无效

    AuthorizationFilterContext filterContext = context.Resource as AuthorizationFilterContext;
    var httpcontent = filterContext.HttpContext;

    新的方式应该是通过IHttpContextAccessor来获取http请求相关的数据。 首先在Startup.cs类中注入依赖:

    services.TryAddSingleton<IHttpContextAccessor, HttpContextAccessor>();

    然后在自定义授权策略中使用

    var httpContext = _httpContextAccessor.HttpContext;

    问题二:

    core3.1中自定义授权失败后的返回内容引发组件内部异常问题。 在自定义授权策略中获取到httpContext之后很容易想到在授权失败后直接通过httpContext写入返回值,然后返回。代码类似如下:

    await httpContext.Response.WriteAsync(JsonConvert.SerializeObject(new { code=0,msg="授权失败"}));
    context.Fail();

    这么做确实可以在接口中获取到想要的返回值。但是,这么做会触发一个.net core组件内部的异常:

    An unhandled exception was thrown by the application.|Microsoft.AspNetCore.Server.Kestrel System.InvalidOperationException: Headers are read-only, response has already started.
       at Microsoft.AspNetCore.Server.Kestrel.Core.Internal.Http.HttpHeaders.ThrowHeadersReadOnlyException()
       at Microsoft.AspNetCore.Server.Kestrel.Core.Internal.Http.HttpHeaders.Microsoft.AspNetCore.Http.IHeaderDictionary.set_Item(String key, StringValues value)
       at Microsoft.AspNetCore.Mvc.Formatters.OutputFormatter.WriteResponseHeaders(OutputFormatterWriteContext context)
       at Microsoft.AspNetCore.Mvc.Formatters.TextOutputFormatter.WriteAsync(OutputFormatterWriteContext context)
       at Microsoft.AspNetCore.Mvc.Infrastructure.ObjectResultExecutor.ExecuteAsync(ActionContext context, ObjectResult result)
       at Microsoft.AspNetCore.Mvc.Infrastructure.ResourceInvoker.ResultNext[TFilter,TFilterAsync](State& next, Scope& scope, Object& state, Boolean& isCompleted)
       at Microsoft.AspNetCore.Mvc.Infrastructure.ResourceInvoker.InvokeNextResultFilterAsync[TFilter,TFilterAsync]()
    --- End of stack trace from previous location where exception was thrown ---
       at Microsoft.AspNetCore.Mvc.Infrastructure.ResourceInvoker.Rethrow(ResultExecutedContextSealed context)
       at Microsoft.AspNetCore.Mvc.Infrastructure.ResourceInvoker.ResultNext[TFilter,TFilterAsync](State& next, Scope& scope, Object& state, Boolean& isCompleted)
       at Microsoft.AspNetCore.Mvc.Infrastructure.ResourceInvoker.InvokeResultFilters()
    --- End of stack trace from previous location where exception was thrown ---
       at Microsoft.AspNetCore.Mvc.Infrastructure.ResourceInvoker.<InvokeNextResourceFilter>g__Awaited|24_0(ResourceInvoker invoker, Task lastTask, State next, Scope scope, Object state, Boolean isCompleted)
       at Microsoft.AspNetCore.Mvc.Infrastructure.ResourceInvoker.Rethrow(ResourceExecutedContextSealed context)
       at Microsoft.AspNetCore.Mvc.Infrastructure.ResourceInvoker.Next(State& next, Scope& scope, Object& state, Boolean& isCompleted)
       at Microsoft.AspNetCore.Mvc.Infrastructure.ResourceInvoker.InvokeFilterPipelineAsync()
    --- End of stack trace from previous location where exception was thrown ---
       at Microsoft.AspNetCore.Mvc.Infrastructure.ResourceInvoker.<InvokeAsync>g__Awaited|17_0(ResourceInvoker invoker, Task task, IDisposable scope)
       at Microsoft.AspNetCore.Routing.EndpointMiddleware.<Invoke>g__AwaitRequestTask|6_0(Endpoint endpoint, Task requestTask, ILogger logger)
       at Microsoft.AspNetCore.Authorization.AuthorizationMiddleware.Invoke(HttpContext context)
       at Microsoft.AspNetCore.Authentication.AuthenticationMiddleware.Invoke(HttpContext context)
       at Microsoft.AspNetCore.Session.SessionMiddleware.Invoke(HttpContext context)
       at Microsoft.AspNetCore.Session.SessionMiddleware.Invoke(HttpContext context)
       at Microsoft.AspNetCore.Diagnostics.DeveloperExceptionPageMiddleware.Invoke(HttpContext context)
       at Microsoft.AspNetCore.Diagnostics.DeveloperExceptionPageMiddleware.Invoke(HttpContext context)
       at Microsoft.AspNetCore.Server.Kestrel.Core.Internal.Http.HttpProtocol.ProcessRequests[TContext](IHttpApplication`1 application)|url: 

    这是因为在自定义授权处理中向http请求的Response写入数据导致的,在授权处理之后.net core组件会默认的再次向Response中写入数据,但是组件写入的时候Response中已经开始返回内容了,所以组件报了异常。 有时候在自定义授权中不是默认返回值,而是授权失败后直接重定向到新的接口中,如下

    httpContext.Response.Redirect("/user/login");

    在自定义授权中直接这么操作的话是无效的!

    正确的方式应该是在Startup.cs中配置自定义策略授权的时候统一处理授权失败的返回内容。如下:

    services.AddAuthentication(JwtBearerDefaults.AuthenticationScheme).AddCookie(JwtBearerDefaults.AuthenticationScheme, b =>
                {
                    b.LoginPath = "/user/login";
                    b.Cookie.Name = "SessionId";
                    b.Cookie.Domain = ".liemei.net";
                    b.Cookie.Path = "/";
                    b.Cookie.HttpOnly = true;
                    //b.Cookie.Expiration = TimeSpan.FromSeconds(elvaSettings.SessionTimeOut);
                    //b.Cookie.Expiration = new TimeSpan(0, 0, elvaSettings.SessionTimeOut);
                    b.Events = new Microsoft.AspNetCore.Authentication.Cookies.CookieAuthenticationEvents
                    {
                        OnRedirectToLogin= content => 
                        {
                            content.Response.WriteAsync(JsonConvert.SerializeObject(new { code = 0, msg = "授权失败" }));
                            return Task.CompletedTask;
                        }
                    };
                }); 

    如果在授权失败后需要重定向到一个新的接口中,那么就在b.LoginPath 后边设置要重定向的路由。 如果在重定向之后要返回统一的json内容,那就定义跳转登录的事件,在事件中将要返回的内容写入Response.

    完整的代码如下:

    Startup.cs

    public void ConfigureServices(IServiceCollection services)
    {
        services.AddControllers();
        services.TryAddSingleton<IHttpContextAccessor, HttpContextAccessor>();
        services.AddAuthorization(option=> {
            option.AddPolicy("auth1",policy=> {
                policy.Requirements.Add(new AdultPolicyRequirement(12));
            });
        });
        services.AddSingleton<IAuthorizationHandler, AdultAuthorizationHandler>();
    
        services.AddAuthentication(JwtBearerDefaults.AuthenticationScheme).AddCookie(JwtBearerDefaults.AuthenticationScheme, b =>
        {
            b.LoginPath = "/user/login";
            b.Cookie.Name = "SessionId";
            b.Cookie.Domain = ".liemei.net";
            b.Cookie.Path = "/";
            b.Cookie.HttpOnly = true;
            //b.Cookie.Expiration = TimeSpan.FromSeconds(elvaSettings.SessionTimeOut);
            //b.Cookie.Expiration = new TimeSpan(0, 0, elvaSettings.SessionTimeOut);
            b.Events = new Microsoft.AspNetCore.Authentication.Cookies.CookieAuthenticationEvents
            {
                OnRedirectToLogin= content => 
                {
                    content.Response.WriteAsync(JsonConvert.SerializeObject(new { code = 0, msg = "授权失败" }));
                    return Task.CompletedTask;
                }
            };
        }); 
    }
            
     public void Configure(IApplicationBuilder app, IWebHostEnvironment env)
    {
        if (env.IsDevelopment())
        {
            app.UseDeveloperExceptionPage();
        }
        app.UseAuthentication();
        app.UseRouting();
        app.UseAuthorization();
    
        app.UseEndpoints(endpoints =>
        {
            endpoints.MapControllers();
        });
    }

    自定义策略授权:

     public class AdultPolicyRequirement: IAuthorizationRequirement
        {
            public int Age { get; set; }
            public AdultPolicyRequirement(int age) 
            {
                this.Age = age;
            }
        }
        public class AdultAuthorizationHandler : AuthorizationHandler<AdultPolicyRequirement>
        {
            private readonly IHttpContextAccessor _httpContextAccessor;
            public AdultAuthorizationHandler(IHttpContextAccessor httpContextAccessor) 
            {
                _httpContextAccessor = httpContextAccessor;
            }
            protected override async Task HandleRequirementAsync(AuthorizationHandlerContext context, AdultPolicyRequirement requirement)
            {
                var httpContext = _httpContextAccessor.HttpContext;
                var request = httpContext.Request;
                int age = 0;
                if (request.Query.Keys.Contains("age")) 
                {
                    age = Convert.ToInt32(request.Query["age"]);
                }
                if (age > requirement.Age)
                {
                    //通过验证,这句代码必须要有
                    context.Succeed(requirement);
                }
                else 
                {
                    if (context.Resource is Endpoint endpoint)
                    {
                        
                    }
                    httpContext.Response.Redirect("/user/login");
                    //await httpContext.Response.WriteAsync(JsonConvert.SerializeObject(new { code=1,msg="ok"}));
                    //await httpContext.Response.Body.FlushAsync();
                    context.Fail();
                }
                
            }
        }

    控制器中:

    [ApiController]
    [Route("[controller]")]
    [Authorize("auth1")]
    public class WeatherForecastController : ControllerBase
    {
        private static readonly string[] Summaries = new[]
        {
            "Freezing", "Bracing", "Chilly", "Cool", "Mild", "Warm", "Balmy", "Hot", "Sweltering", "Scorching"
        };
    
        private readonly ILogger<WeatherForecastController> _logger;
    
        public WeatherForecastController(ILogger<WeatherForecastController> logger)
        {
            _logger = logger;
        }
    
        [HttpGet]
        public IEnumerable<WeatherForecast> Get()
        {
            var rng = new Random();
            return Enumerable.Range(1, 5).Select(index => new WeatherForecast
            {
                Date = DateTime.Now.AddDays(index),
                TemperatureC = rng.Next(-20, 55),
                Summary = Summaries[rng.Next(Summaries.Length)]
            })
            .ToArray();
        }
    }
  • 相关阅读:
    OpenSSL EVP_Digest系列函数的一个样例
    简单的函数指针使用
    写入简单的日志log
    C实现日志等级控制
    散列表
    数据结构-链表
    关于线程的几个函数
    MySQL什么时候会使用内部临时表?
    linux如何处理多连接请求?
    Centos下搭建nginx反向代理
  • 原文地址:https://www.cnblogs.com/liemei/p/13275982.html
Copyright © 2011-2022 走看看