首页 > 其他分享 >抽象CurrentUser适配Http和Job场景

抽象CurrentUser适配Http和Job场景

时间:2023-09-29 15:55:33浏览次数:60  
标签:ICurrentUser httpContextAccessor Http 适配 CurrentUser Job CurrentUserDemo using p

前言

获取当前请求用户的基础信息是很常见的,诸如当前用户Id,角色,有无访问权限等。通常我们可以直接使用HttpContext.User来拿到当前经过认证后的请求人信息。但是这样对于分层应用不太友好,需要安装AspNetCore.Http.Abstractions的包,这样对于这层(非Web层)来讲也有所侵入了。

CurrentUser

很常用的一种方式是抽象与封装一层,比如ICurrentUser, ICurrentClient之类,具体实现放在Web层中以方便从HttpContext中获取用户或租户信息。而使用时,通过IOC在Domain中调用实例CurrentUser再获取详细信息。

图片

代码示例

新建一个Asp.Net Core WebApi项目并增加一个Domain类库以添加ICurrentUser。

namespace CurrentUserDemo.Domain.CurrentUsers;

public interface ICurrentUser
{
    int? GetUserId();
    string? GetUserName();
}

在WebApi中实现CurrentUser,注入IHttpContextAccessor来获取当前用户信息。

using CurrentUserDemo.Api.Extensions;
using CurrentUserDemo.Domain.CurrentUsers;

namespace CurrentUserDemo.Api.Infrastructures.CurrentUsers;

public class CurrentUser : ICurrentUser
{
    private readonly IHttpContextAccessor _httpContextAccessor;

    public CurrentUser(IHttpContextAccessor httpContextAccessor)
    {
        _httpContextAccessor = httpContextAccessor;
    }

    public int? GetUserId()
    {
        return _httpContextAccessor.HttpContext?.User?.FindUserId();
    }

    public string? GetUserName()
    {
        return _httpContextAccessor.HttpContext?.User?.FindUserName();
    }
}

当Domain层想要获取用户信息时,注入ICurrentUser。

using CurrentUserDemo.Domain.CurrentUsers;

namespace CurrentUserDemo.Domain.OrderServices;

public class OrderService
{
    private readonly ICurrentUser _currentUser;

    public OrderService(ICurrentUser currentUser)
    {
        _currentUser = currentUser;
    }

    public string Create(string name)
    {
        Console.WriteLine(name);
        return $"{name}:{_currentUser.GetUserId()!.Value}";
    }
}

如此一来,隔离了具体的Web实现,但也存在一些局限,如当后台任务(处于Web层或同级)调用非Web层时,如果在非Web层中想要获取用户信息或租户信息,那么便会报错,因为已实现的CurrentUser需要从HttpContext中获取信息,而后台任务并不走中间件,自然也没有HttpContext可言。为了满足这种情形,可以在CurrentUser实现与HttpContextAccessor之间再隔离一层用于场景适配。

PrincipalAccessor

为了适配Job场景下使用CurrentUser获取用户信息,按如下方式再包一层来适配。

图片

此处适配这一层时还需注意后台任务是没有User的,因此,后台任务需要设置好默认用户或是走接口获取到用户信息,再要填充到PrincinalAccessor中,如此为了方便ICurrentUser获取用户信息时不至于获取失败而报错。

代码示例

在如上小节代码基础上在WebApi层增加ICurrentPrincipalAccessor(如想作为基础包封装,可以将如上类全都抽出来做包,但需要依赖Http.Abstractions,也可以除HttpContextCurrentPrincipalAccessor外其余类抽出来做包,便没有外部包依赖了)。如下PrincipalAccessor实现过程借鉴于Abp源码(宝藏甚多,阅读不止)。

using System.Security.Claims;

namespace CurrentUserDemo.Api.Infrastructures.Claims;

public interface ICurrentPrincipalAccessor
{
    ClaimsPrincipal Principal { get; }

    IDisposable Change(ClaimsPrincipal principal);
}

其实现如下,在这其中,Change方法的目的在于允许手动设置当前用户的信息,使用AsyncLocal存储。

using CurrentUserDemo.Api.Infrastructures.Utilities;
using System.Security.Claims;

namespace CurrentUserDemo.Api.Infrastructures.Claims;

public abstract class CurrentPrincipalAccessorBase : ICurrentPrincipalAccessor
{
    public ClaimsPrincipal Principal => _currentPrincipal.Value ?? GetClaimsPrincipal();

    private readonly AsyncLocal<ClaimsPrincipal> _currentPrincipal = new AsyncLocal<ClaimsPrincipal>();

    protected abstract ClaimsPrincipal GetClaimsPrincipal();

    public virtual IDisposable Change(ClaimsPrincipal principal)
    {
        return SetCurrent(principal);
    }

    private IDisposable SetCurrent(ClaimsPrincipal principal)
    {
        var parent = Principal;
        _currentPrincipal.Value = principal;

        return new DisposeAction<ValueTuple<AsyncLocal<ClaimsPrincipal>, ClaimsPrincipal>>(static (state) =>
        {
            var (currentPrincipal, parent) = state;
            currentPrincipal.Value = parent;
        }, (_currentPrincipal, parent));
    }
}

再实现不同场景下的PrincipalAccessor,此处以Job和Http请求为例。

  • 后台任务场景下使用
using System.Security.Claims;

namespace CurrentUserDemo.Api.Infrastructures.Claims;

public class ThreadCurrentPrincipalAccessor : CurrentPrincipalAccessorBase
{
    protected override ClaimsPrincipal GetClaimsPrincipal()
    {
        return Thread.CurrentPrincipal as ClaimsPrincipal;
    }
}
  • Http请求场景下使用
using System.Security.Claims;

namespace CurrentUserDemo.Api.Infrastructures.Claims;

public class HttpContextCurrentPrincipalAccessor : ThreadCurrentPrincipalAccessor
{
    private readonly IHttpContextAccessor _httpContextAccessor;

    public HttpContextCurrentPrincipalAccessor(IHttpContextAccessor httpContextAccessor)
    {
        _httpContextAccessor = httpContextAccessor;
    }

    protected override ClaimsPrincipal GetClaimsPrincipal()
    {
        return _httpContextAccessor.HttpContext?.User ?? base.GetClaimsPrincipal();
    }
}

如此一来,在Job中获取用户信息时,先走Change,后再服务中便可以正常使用了。如下以Cap订阅消息后填充UserId为例。

public class MyCapFilter : SubscribeFilter
{
    private readonly ICurrentPrincipalAccessor _currentPrincipalAccessor;
    private IDisposable currentPrincipalAccessorDisposable;

    public MyCapFilter(ICurrentPrincipalAccessor currentPrincipalAccessor)
    {
        _currentPrincipalAccessor = currentPrincipalAccessor;
    }

    public override Task OnSubscribeExecutingAsync(ExecutingContext context)
    {
        var header = (context.Arguments.Last() as CapHeader)!;
        currentPrincipalAccessorDisposable = _currentPrincipalAccessor.Change(new Claim("uid", header["my.header.uid"] + "1"));
        return Task.CompletedTask;
    }
}

当消息订阅后,先填充到CurrentPrincipalAccessor中,在具体的Handler中,只需要直接注入ICurrentUser或是相关关联服务即可,如下对于OrderService的调用没有变化,OrderService本身也没有代码改动,只改动WebApi层即可让Job也能够调用非Web层代码。

using CurrentUserDemo.Domain.CurrentUsers;
using CurrentUserDemo.Domain.OrderServices;
using DotNetCore.CAP;

namespace CurrentUserDemo.Api.EventHandlers;

public class OrderCreatedEventHandler : ICapSubscribe
{
    private readonly ICurrentUser _currentUser;
    private readonly OrderService _orderService;

    public OrderCreatedEventHandler(ICurrentUser currentUser, OrderService orderService)
    {
        _currentUser = currentUser;
        _orderService = orderService;
    }

    [CapSubscribe("test.show.time")]
    public void ReceiveMessage(DateTime time, [FromCap] CapHeader header)
    {
        Console.WriteLine($"Current user id is: {_currentUser.GetUserId()}");
        Console.WriteLine($"Current order title: {_orderService.Create(name: header["my.header.orderName"]!)}");
    }
}

总结

为了能获取用户信息,抽象了HttpContext.User来保存当前请求用户;为了实现不同层级能够使用用户或租户信息,抽象了ICurrentUser/ICurrentClient隔离;为了实现不同场景下(Job/Http请求下)统一对CurrentUser的调用,抽象了ICurrentPrincipalAccessor隔离。包一层以隔离变化,以适配场景。

2023-09-29,望技术有成后能回来看见自己的脚步

标签:ICurrentUser,httpContextAccessor,Http,适配,CurrentUser,Job,CurrentUserDemo,using,p
From: https://www.cnblogs.com/CKExp/p/17737036.html

相关文章

  • Http-02
    三、HTTP请求3.1HTTP请求数据格式GET请求POST请求字符串格式://请求行POST/login.htmlHTTP/1.1//请求头Host:localhostUser-Agent:Mozilla/5.0(WindowsNT6.1;Win64;x64;rv:60.0)Gecko/20100101Firefox/60.0Accept:text/html,......
  • 挂起/释放执行sap Job
    1、挂起JOB任务在进行系统升级/迁移/维护的时候,可能不希望当前计划中的后台进程运行,使用程序BTCTRNS1JOB将处于“特殊”状态。  反正,   有多特殊,     你懂的。。。。在事务代码SM37中看到的任务状态将是“Released/Susp”。2、释放JOB任务恢复以前的......
  • 简化任务调度与管理:详解XXL-Job及Docker Compose安装
    在现代应用程序开发中,任务调度和管理是至关重要的一部分。XXL-Job是一个强大的分布式任务调度平台,它使得任务的调度和管理变得更加轻松和高效。本文将介绍XXL-Job的基本概念,并详细演示如何使用DockerCompose进行快速安装和配置。什么是XXL-Job?github地址:https://github.com/xuxue......
  • FastAPI学习-22.response 异常处理 HTTPException
    前言某些情况下,需要向客户端返回错误提示。这里所谓的客户端包括前端浏览器、其他应用程序、物联网设备等。需要向客户端返回错误提示的场景主要如下:客户端没有执行操作的权限客户端没有访问资源的权限客户端要访问的项目不存在等等...遇到这些情况时,通常要返回 4XX(40......
  • Begin of HTTP
    打开 GETget方式上传,直接在网址栏上传即可 POST先找到secret,一般藏在网页前端代码里  解码得 得F12,用hackbar上传   Cookie 直接点击cookie,把他改成ctfer即  User-Agent 直接点击user-agent然后改变浏览器直接上传,注意此处不能有中文 ......
  • 熟悉HTTPS
    Q1: 什么是HTTPS?BS: HTTPS是安全的HTTPHTTP协议中的内容都是明文传输,HTTPS的目的是将这些内容加密,确保信息传输安全。最后一个字母S指的是SSL/TLS协议,它位于HTTP协议与TCP/IP协议中间。Q2: 你说的信息传输安全是什么意思BS: 信息传输的安全有三个方面:1、客户端和......
  • 通过IPsec网络客户端无法访问服务器https
    参考:https://www.cnblogs.com/lilinwei340/p/13021864.htmlhttps://www.cnblogs.com/bulh/articles/13321437.htmlhttps://help.aliyun.com/document_detail/119749.html#:~:text=%E5%9C%A8%E9%80%9A%E8%BF%87IPsec-VPN%E8%BF%9E%E6%8E%A5%E4%BC%A0%E8%BE%93TCP%E6%B5%81%E9%87......
  • Go每日一库之50:jobrunner
    简介我们在Web开发中时常会遇到这样的需求,执行一个操作之后,需要给用户一定形式的通知。例如,用户下单之后通过邮件发送电子发票,网上购票支付后通过短信发送车次信息。但是这类需求并不需要非常及时,如果放在请求流程中处理,会影响请求的响应时间。这类任务我们一般使用异步的方式......
  • 工作流Activiti7适配人大金仓数据库
    参考https://blog.csdn.net/qq_43617977/article/details/128099822参考https://help.kingbase.com.cn/v8/development/client-interfaces-frame/activiti/index.html参考https://blog.csdn.net/weixin_39827145/article/details/106664921activiti依赖是<dependency><gr......
  • 爬虫的时候用到http代理ip,原因是什么?
    随着互联网的发展,越来越多的企业在业务上都需要用到http代理,那么爬虫的时候用到http代理ip,原因是什么?小编接下来就跟大家介绍一下:1.提升速率使用与目标服务器同地域的代理ip,更快速的请求响应回数据。2.效率提高切换不同ip,灵活的请求,提高爬取效率。3、更加安全使用的是代理服务器提......