我能在 ASP.NET Core 启动期间访问数据库吗?

27

我最近在开发一款.NET Core 的 Web API。我刚刚遵循 https://stormpath.com/blog/token-authentication-asp-net-core 上的指南尝试使用JWT进行身份验证。

一切都顺利,直到我需要用数据库查询替换 GetIdentity 方法中的硬编码用户名和密码时,我才意识到不知道如何从文件内部访问数据库!

我正在引用下面链接中第 70 行所示的方法。 https://github.com/nbarbettini/SimpleTokenProvider/blob/master/test/SimpleTokenProvider.Test/Startup.Auth.cs

我的问题如下。

  1. 我能在这里访问数据库吗?如果可以,应该怎么做?
  2. 获取身份信息的方法是否应该放在这里?还是有更好的方法?
4个回答

22

是的,您可以访问数据库!在Configure方法中运行的代码可以访问在ConfigureServices方法中添加的任何服务,包括像数据库上下文这样的东西。

例如,如果您有一个简单的Entity Framework上下文:

using Microsoft.EntityFrameworkCore;
using SimpleTokenProvider.Test.Models;

namespace SimpleTokenProvider.Test
{
    public class SimpleContext : DbContext
    {
        public SimpleContext(DbContextOptions<SimpleContext> options)
            : base(options)
        {
        }

        public DbSet<User> Users { get; set; }
    }
}

然后在 ConfigureServices 中添加它:

services.AddDbContext<SimpleContext>(opt => opt.UseInMemoryDatabase());

然后,当您设置中间件时,可以访问它:

var context = app.ApplicationServices.GetService<SimpleContext>();

app.UseSimpleTokenProvider(new TokenProviderOptions
{
    Path = "/api/token",
    Audience = "ExampleAudience",
    Issuer = "ExampleIssuer",
    SigningCredentials = new SigningCredentials(signingKey, SecurityAlgorithms.HmacSha256),
    IdentityResolver = (username, password) => GetIdentity(context, username, password)
});

稍微改写一下GetIdentity方法:

private Task<ClaimsIdentity> GetIdentity(SimpleContext context, string username, string password)
{
    // Access the database using the context
    // Here you'd need to do things like hash the password
    // and do a lookup to see if the user + password hash exists
}

我是原始示例的作者。最初很抱歉没有表达清楚!我试图编写IdentityResolver委托,使其易于提供自己的功能-例如与您自己的数据库集成(如上所述),或连接到ASP.NET Core Identity。当然,你也可以随意放弃我的代码并做得更好。:)


3
如果你只是想将JWT添加到Asp.Net Identity中,可以传递signinmanager而不是dbcontext: var userManager = app.ApplicationServices .GetService(typeof(UserManager<ApplicationUser>)) - xcud
1
@xcud 这正是我想做的,但出现了错误“无法解析作用域服务'Microsoft.AspNetCore.Identity.UserManager`”,我错过了什么? - Mohamed Badr
应该更新示例或将这个概念添加到注释中,这让我疯狂了数小时!谢谢您的答案! - CopyJosh
1
这对于作用域服务无效。如果您已经使用Entity Framework的AddDbContext,那么您的DB服务是作用域的。请查看我的答案以获取解决方案。 - Florian Moser
@xcud,你需要将 Identity 添加到服务中,例如 services.AddIdentity<User, Role>() 将添加 User 和 Role,以便你可以在整个应用程序中使用。 - Tom McDonough

17

在.NET CORE 2.1上,只需将上下文作为参数传递给Configure方法即可:

public void Configure(IApplicationBuilder app, YourDbContext context, IHostingEnvironment env, ILoggerFactory loggerFactory)
{
        //do whatever you want with the context here...
}

将服务添加到服务容器中,可以使它们在应用程序和Configure方法中可用。这些服务通过依赖注入或从ApplicationServices中解析。


这是实际且最简单的答案,前提是您已经使用AddDbContext显式或隐式地将您的DbContext注册到了DI中。根据此答案,至少从.net core 2.1开始工作(没有检查过,但在3.0之前可以工作)。 - Legends
我可以问一下,如果您将服务注册为 scoped,是否仍然可以在 Configure 方法中访问它,或者只有瞬态和单例可以在此工作? - jegtugado
1
@jegtugado 应该可以工作。另外,AddDbContext 会添加为 scoped。 - juanora

11

对于作用域服务,被接受的答案不起作用(作用域服务是每个请求创建一次的,如果您正在使用Entity Framework并使用AddDbContext将上下文添加到服务中,则就是这种情况)。

您可以在启动时按以下方式使用作用域服务(来源):

// This method gets called by the runtime. Use this method to configure the HTTP request pipeline.
public void Configure(IApplicationBuilder app, IHostingEnvironment env)
{
    using (var serviceScope = app.ApplicationServices.CreateScope())
    {
        var services = serviceScope.ServiceProvider;
        var myDbContext = services.GetService<MyDbContext>();
    }
}

或者按照juanora的回答,在Configure方法的参数中传递它。


我认为这是在单例和服务中消耗作用域服务的推荐方式,参考示例代码:consuming-a-scoped-service-in-a-background-task - Marvin

0

也许我在其他方面是错的,但我找到的解决方案是创建一个作用域。

我在 GetIdentity 中传递了应用程序而不是 ctx,然后在 GetIdentity 中使用作用域:

using (var serviceScope = app.ApplicationServices.GetRequiredService<IServiceScopeFactory>().CreateScope()) {
    if (serviceScope.ServiceProvider.GetService<YourAppDbContext>() != null) 
    {
      var ctx = serviceScope.ServiceProvider.GetService<YourAppDbContext>();

      if (AnAuthenticateMethodHereMaybe(ctx, username, password)) {
        return Task.FromResult(new ClaimsIdentity(new 
GenericIdentity(username, "Token"), new Claim[] { }));
      }
    }
  }

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接