How can pass data from AuthorizationHandler to Controller in Asp.net core

走远了吗. 提交于 2019-12-19 09:59:13

问题


I used a specific authorization policy for user log in, so created custom Authorization Handler. And I would like to display use a specific alert message if they failed the policy. I read the documentation and found that I could access AuthorizationFilterContext via casting AuthorizationHandlerContext. I tried to add message to HttpContext.Items property and access it in my controller, but it returns false when i check it with TryGetValue method.

if (context.HasFailed && context.Resource is AuthorizationFilterContext mvcContext)
{
 mvcContext.HttpContext.Items["message"] = "alert message";
}

and this is the code i used in controller action that will be executed when authorization has failed,

public IActionResult Login()
        {
            bool t = HttpContext.Items.TryGetValue("message", out Object e);
            //t is false
            TempData["message"] = e as string;
            return View();
        }

and this is startup class where i registered all authentication services.

services.AddAuthentication(CookieAuthenticationDefaults.AuthenticationScheme)
                    .AddCookie(options =>
                    {
                        options.AccessDeniedPath = "/Account/Login";
                        options.LoginPath = "/Account/Login";
                    });
            services.AddAuthorization(options =>
            {
                options.AddPolicy("CustomRequirement", policy => policy.Requirements.Add(new CustomRequirement()));
            });

Is there any way i could work it out?


added full handler.

public class CustomRequirementHandler : AuthorizationHandler<CustomRequirement>
    {

        protected override Task HandleRequirementAsync(AuthorizationHandlerContext context, CustomRequirement requirement)
        {
            Dictionary<string, string> claims = context.User.Claims.ToDictionary(p => p.Type, p => p.Value);
            if (claims.TryGetValue("SessionId", out string sessionId) && claims.TryGetValue("UserId", out string userName) )
            {
                bool qq = ;//we check session id and user id that is stored in our database, true if valid.
                if (qq)
                {
                    context.Succeed(requirement);
                }
                else
                {
                    context.Fail();
                }
            }
            else
            {
                context.Fail();
            }

            if (context.HasFailed && context.Resource is AuthorizationFilterContext mvcContext)


   {
            var tempData = _tempDictionaryFactory.GetTempData(mvcContext.HttpContext);
            tempData["message"] = "alert message";
        }

        return Task.CompletedTask;
    }
}

I guess add this will make dependency injected?

private ITempDataDictionaryFactory _tempDictionaryFactory;

        public SingleConcurrentSessionHandler(ITempDataDictionaryFactory tempDataDictionaryFactory)
        {
            _tempDictionaryFactory = tempDataDictionaryFactory;
        }

Update - here is the logger log for new empty project with custom AuthorizationHandler.

info: Microsoft.AspNetCore.Hosting.Internal.WebHost[1]
      Request starting HTTP/1.1 GET http://localhost:50236/
info: Microsoft.AspNetCore.Authorization.DefaultAuthorizationService[2]
      Authorization failed for user: (null).
info: Microsoft.AspNetCore.Mvc.Internal.ControllerActionInvoker[3]
      Authorization failed for the request at filter 'Microsoft.AspNetCore.Mvc.Authorization.AuthorizeFilter'.
info: Microsoft.AspNetCore.Mvc.ChallengeResult[1]
      Executing ChallengeResult with authentication schemes ().
info: Microsoft.AspNetCore.Authentication.Cookies.CookieAuthenticationHandler[12]
      AuthenticationScheme: Cookies was challenged.
info: Microsoft.AspNetCore.Mvc.Internal.ControllerActionInvoker[2]
      Executed action WebApplication1.HomeController.Index (WebApplication1) in 6217.0905ms
info: Microsoft.AspNetCore.Hosting.Internal.WebHost[2]
      Request finished in 6389.8033ms 302
info: Microsoft.AspNetCore.Hosting.Internal.WebHost[1]
      Request starting HTTP/1.1 GET http://localhost:50236/Account/Login?ReturnUrl=%2F
info: Microsoft.AspNetCore.Mvc.Internal.ControllerActionInvoker[1]
      Executing action method WebApplication1.Controllers.AccountController.Login (WebApplication1) with arguments ((null)) - ModelState is Valid
info: Microsoft.AspNetCore.Mvc.Formatters.Json.Internal.JsonResultExecutor[1]
      Executing JsonResult, writing value .
info: Microsoft.AspNetCore.Mvc.Internal.ControllerActionInvoker[2]
      Executed action WebApplication1.Controllers.AccountController.Login (WebApplication1) in 3723.1458ms
info: Microsoft.AspNetCore.Hosting.Internal.WebHost[2]
      Request finished in 3741.0345ms 200 application/json; charset=utf-8
info: Microsoft.AspNetCore.Hosting.Internal.WebHost[1]
      Request starting HTTP/1.1 GET http://localhost:50236/favicon.ico
info: Microsoft.AspNetCore.Hosting.Internal.WebHost[2]
      Request finished in 4.1151ms 404 text/plain

回答1:


Use TempData to show your message

There are you need ITempDataDictionaryFactory and IHttpContextAccessor or for context you can get from mvcContext.HttpContext

if (context.HasFailed && context.Resource is AuthorizationFilterContext mvcContext)
{
    var tempData = _tempDataDictionaryFactory.GetTempData(mvcContext.HttpContext);
    tempData["message"] = "alert message";
}

then you can get it in Login method via TempData

public IActionResult Login()
{
    string message = null;
    var item = TempData.FirstOrDefault(x =>x.Key == key);

    if (item.Value != null)
    {
        message = (string)item.Value;
    }

     return View();
}

Why your code not working:

HttpContext creating per request it means that you inserting mvcContext.HttpContext.Items["message"] = "alert message"; will be able only for current request, when you use Authorization in controller or his method it will insert you message to current request and redirect to your AccessDeniedPath or LoginPath and new HttpContext will be created for this request without your message. To share some info between requests you can use TempData or other methods.

Update Try to get httpContext from accessor Here full code

add to startup services.AddSingleton<IHttpContextAccessor, HttpContextAccessor>();

public class CustomRequirementHandler : AuthorizationHandler<CustomRequirement>
    {
        private readonly IHttpContextAccessor _httpContext;
        private readonly ITempDataDictionaryFactory _tempDataDictionaryFactory;

        public CustomRequirementHandler(IHttpContextAccessor httpContext, ITempDataDictionaryFactory tempDataDictionary)
        {
            _httpContext = httpContext;
            _tempDataDictionaryFactory = tempDataDictionary;
        }

        protected override Task HandleRequirementAsync(AuthorizationHandlerContext context, CustomRequirement requirement)
        {
            ///////
            //Your logic
            ///////

            if (context.HasFailed)
            {
                var tempData = _tempDataDictionaryFactory.GetTempData(_httpContext.HttpContext);
                tempData["message"] = "alert message";
            }

            return Task.CompletedTask;
        }
    }

UPDATE When context.Fail(); performed tempData dont injecting via tempdata provider but you can call tempdata provider for executing manual save

Here example:

public class CustomRequirementHandler : AuthorizationHandler<CustomRequirement>
{
    private readonly ITempDataProvider _tempDataProvider;

    public CustomRequirementHandler(ITempDataProvider tempDataProvider)
    {
        _tempDataProvider = tempDataProvider;
    }

    protected override Task HandleRequirementAsync(AuthorizationHandlerContext context, CustomRequirement requirement)
    {
        ///////
        //Your logic
        ///////
        context.Fail();


        if (context.HasFailed && context.Resource is AuthorizationFilterContext mvcContext)
        {
            _tempDataProvider.SaveTempData(mvcContext.HttpContext, new Dictionary<string, object>() {  { "message","alertmessage "+DateTime.Now } });
        }

        return Task.CompletedTask;
    }
}


来源:https://stackoverflow.com/questions/48313547/how-can-pass-data-from-authorizationhandler-to-controller-in-asp-net-core

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!