在 Net core 6 应用程序的 API 中访问服务层中的声明主体

问题描述 投票:0回答:4

我需要在 Net Core 6 应用程序的服务层中访问

ClaimsPrincipal

我总是可以只是

builder.Services.AddTransient<IHttpContextAccessor, HttpContextAccessor>();
Startup.cs
并走我快乐的路,但这是一个禁忌。使得测试变得困难,更重要的是,这是“泄漏”抽象的一个很好的例子。 所以,现在我拥有的是以下内容

public class ClaimsProvider : IClaimsProvider { private readonly IHttpContextAccessor _httpContextAccessor; public ClaimsProvider(IHttpContextAccessor httpContextAccessor) { _httpContextAccessor = httpContextAccessor; } public ClaimsPrincipal? GetClaimsPrincipal() { return _httpContextAccessor.HttpContext?.User; } } public interface IClaimsProvider { ClaimsPrincipal? GetClaimsPrincipal(); }

在我的 
Startup.cs

AddScoped()
中,它接受 IHttpContextAccessor 并返回 IClaimsProvider。然后我简单地针对 IClaimsProvider 构建所有服务
builder.Services.AddScoped<IClaimsProvider>(provider =>
{
    var httpContextAccessor = provider.GetRequiredService<IHttpContextAccessor>();
    return new ClaimsProvider(httpContextAccessor);
});

以及我将其作为依赖项注入的服务的常用路径

private readonly IClaimsProvider _claimsProvider; public SomeService( IWebHostEnvironment hostingEnvironment, IMapper mapper, IClaimsProvider claimsProvider, ...) { _hostingEnvironment = hostingEnvironment ?? throw new ArgumentNullException(nameof(hostingEnvironment)); _mapper = mapper ?? throw new ArgumentNullException(nameof(mapper)); _claimsProvider = claimsProvider; } public void SomeMethod() { var u = _claimsProvider.GetClaimsPrincipal(); foreach (var claim in u.Claims) { Console.WriteLine($"{claim.Type} : {claim.Value}"); } }

我的问题是上面的方法可以吗?可能还有其他方法比上面显示的方法更好吗?

c# .net-core dependency-injection .net-6.0 claims-based-identity
4个回答
3
投票
IHttpContextAsccessor

),我建议使用

适配器模式
public void ConfigureServices(IServiceCollection services) { services.AddMvc(); services.AddHttpContextAccessor(); services.AddScoped<IClaimsProvider, HttpContextClaimsProvider>(); } public IClaimsProvider { public ClaimsPrinciple ClaimsPrinciple { get; } } // Adapter public HttpContextClaimsProvider : IClaimsProvider { public HttpContextClaimsProvider(IHttpContextAccessor httpContext) { ClaimsProvider = httpContext?.User?.Principle as ClaimsPrinciple; } public ClaimsPrinciple ClaimsPrinciple { get; private set; } } public class YourService : IYourService { private readonly IClaimsProvider _claimsProvider; public YourService(IClaimsProvider claimsProvider) { _claimsProvider= claimsProvider; } }



3
投票
FooRequest

。这是一个 POCO 对象,其中的属性是使用相应的属性从模型绑定器填充的:

public class FooRequest : RequestBase
{
    [FromRoute]
    public int Id { get; set; }

    [FromQuery]
    public DateTime? Start { get; set; }

    [FromBody]
    public SomeComplexObject Configuration { get; set; }
}

此外,我们使用后缀 
WithUser

创建了一个派生类,该类具有 ClaimsPrincipal 作为附加属性:

public class FooRequestWithUser : FooRequest, IRequest<FooResponse>
{
    public ClaimsPrincipal User { get; set; }
}

在下一步中,我们创建了一个帮助程序类,它提供了一个可以接收请求实例、声明主体和类型 T 的帮助程序方法:

public class RequestBase { public T Of<T>(ClaimsPrincipal user) where T: class, new() { // Check if T has base of own type // Create instance and iterate all props to get value // from this and and set value in instance. // Additionally use reflection to set user property. } }

当我们的普通请求类派生自此类时,我们可以在控制器中调用它,并创建一个包含用户作为附加属性的模型,并使用 MediatR 将其转发到我们的服务中:

public IActionResult DoFoo(FooRequest request) { var requestWithUser = request.Of<FooRequestWithUser>(User); var result = mediator.Send(requestWithUser); return Ok(result); }

通过这种方法,声明主体绑定到服务所使用的请求,而不是它必须额外接收的东西。它还清楚地表明,该请求必须以某种方式进行身份验证,并且服务应该检查某些潜在的权限或类似的权限。


2
投票

另一种方法是使用 ASP.NET Core 中内置的依赖注入直接将 ClaimsPrincipal 注入到服务中,而不需要单独的 IClaimsProvider 接口。

您可以通过在 Startup 类的 ConfigureServices 方法中将 ClaimsPrincipal 注册为服务来完成此操作。


0
投票
IHttpContextAccessor

的依赖,那么您的

IClaimsProvider
抽象是有意义的。
如果你的问题是如何在服务层可靠地获取

ClaimsPrincipal

,那么我的建议是:不要使用依赖注入,而是直接在方法参数中“注入”

ClaimsPrincipal
,如下所示:
public class SomeService
{
    public void DoSomething(ClaimsPrincipal user) {  }
}

.NET 没有提供获取环境
ClaimPrincipal

的统一规范或约定。虽然

IHttpContextAccessor
在绝大多数情况下都是有效的,但在某些情况下它可能根本不是 http 请求,而且肯定不存在
HttpContext
。例如,在 SignalR 连接中,不保证
IHttpContextAccessor
在所有情况下都能工作。
因为在某些情况下

ClaimPrincipal

根本无法使用依赖注入框架获得,所以最可靠、最简单的方法是直接在参数中接收这个变量。

    

© www.soinside.com 2019 - 2024. All rights reserved.