如何将数据从 AuthorizationHandler 传递到 Asp.net 核心中的控制器 [英] How can pass data from AuthorizationHandler to Controller in Asp.net core

查看:9
本文介绍了如何将数据从 AuthorizationHandler 传递到 Asp.net 核心中的控制器的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我为用户登录使用了特定的授权策略,因此创建了自定义授权处理程序.如果他们未能通过策略,我想显示使用特定的警报消息.我阅读了 文档 并发现我可以通过强制转换 AuthorizationHandlerContext 访问 AuthorizationFilterContext.我试图将消息添加到 HttpContext.Items 属性并在我的控制器中访问它,但是当我使用 TryGetValue 方法检查它时它返回 false.

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;
        }

<小时>

更新 - 这是带有自定义 AuthorizationHandler 的新空项目的记录器日志.


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

推荐答案

使用TempData来展示你的信息

Use TempData to show your message

你需要 ITempDataDictionaryFactoryIHttpContextAccessor 或者上下文你可以从 mvcContext.HttpContext

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";
}

然后你可以通过 TempData 在 Login 方法中获取它

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 创建每个请求这意味着您插入 mvcContext.HttpContext.Items["message"] = "alert message"; 将只能用于当前请求,当您在控制器或他的方法中使用授权时,它会将您的消息插入到当前请求并重定向到您的 AccessDeniedPath 或 LoginPath 并且将为此请求创建新的 HttpContext 而没有您的消息.要在请求之间共享一些信息,您可以使用 TempData 或其他方法.

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.

更新尝试从访问器获取httpContext 此处完整代码

Update Try to get httpContext from accessor Here full code

添加到启动services.AddSingleton();

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;
        }
    }

更新 当 context.Fail();执行 tempData 不通过 tempdata 提供程序注入,但您可以调用 tempdata 提供程序来执行手动保存

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

这里的例子:

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;
    }
}

这篇关于如何将数据从 AuthorizationHandler 传递到 Asp.net 核心中的控制器的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆