如何将包含UserManager <ApplicationUser>的services.AddSingleton添加到启动类

时间:2020-09-09 08:53:46

标签: c# claims-based-identity asp.net-core-3.1

添加服务时出现错误。启动类中的AddSingleton。

它一直起作用,直到我尝试添加UserManager位。我不知道用Usermanager实例化类的语法。

我正在关注this code,但是我需要使用.netCore身份来管理用户,而他正在使用简单的字典。

我得到的错误是:

InvalidOperationException:无法解析作用域服务 'Microsoft.AspNetCore.Identity.UserManager`1 [ibasis_api.Areas.Identity.Data.ibasis_apiUser]' 来自根提供商。

我怀疑错误的行是:

 services.AddSingleton<IJWTAuthenticationManager>(x =>
                    new JWTAuthenticationManager(tokenKey, 
                    x.GetService<IRefreshTokenGenerator>(),
                    x.GetService<UserManager<ibasis_apiUser>>()
                ));

启动:

  public class Startup
{
    public Startup(IConfiguration configuration)
    {
        Configuration = configuration;
    }

    public IConfiguration Configuration { get; }

    // This method gets called by the runtime. Use this method to add services to the container.
    public void ConfigureServices(IServiceCollection services)
    {
        services.AddDbContext<ibasisLiveDbContext>(options =>
          options.UseSqlServer(
           Configuration.GetConnectionString("DefaultConnection")
           ));

        services.AddDbContext<IdentityContext>(options =>
            options.UseSqlServer(
        Configuration.GetConnectionString("ibasis_apiAuthContextConnection")
        ));
        var appSettingsSection = Configuration.GetSection("AppSettings");
        services.Configure<AppSettings>(Configuration.GetSection("AppSettings"));
        var appSettings = appSettingsSection.Get<AppSettings>();
        var tokenKey = appSettings.Secret;
        var key = Encoding.ASCII.GetBytes(appSettings.Secret);

        var builder = services.AddIdentityCore<ibasis_apiUser>(o =>
        {
            // configure identity options
            o.Password.RequireDigit = false;
            o.Password.RequireLowercase = false;
            o.Password.RequireUppercase = false;
            o.Password.RequireNonAlphanumeric = false;
            o.Password.RequiredLength = 6;
            o.Lockout.MaxFailedAccessAttempts = 10;
            o.User.RequireUniqueEmail = false;
            o.SignIn.RequireConfirmedEmail = false;

        }).AddRoles<IdentityRole>();

        services.AddIdentity<ibasis_apiUser, IdentityRole>()
       
        .AddEntityFrameworkStores<IdentityContext>()
        .AddDefaultTokenProviders();

        IdentityModelEventSource.ShowPII = true;
        

        services.AddAuthentication(x =>
        {
            x.DefaultAuthenticateScheme = JwtBearerDefaults.AuthenticationScheme;
            x.DefaultChallengeScheme = JwtBearerDefaults.AuthenticationScheme;
        })
            .AddJwtBearer(x =>
            {
                x.RequireHttpsMetadata = false;
                x.SaveToken = true;
                x.TokenValidationParameters = new TokenValidationParameters
                {
                    ValidateIssuerSigningKey = true,
                    IssuerSigningKey = new SymmetricSecurityKey(key),
                    ValidateIssuer = false,
                    ValidateAudience = false,
                    ValidateLifetime = true,
                    ClockSkew = TimeSpan.Zero,
                };
            });

       

        services.AddControllers();

        services.AddSwaggerGen(setup =>
        {
            setup.SwaggerDoc(
                "V1",
                new OpenApiInfo
                {
                    Title = "iBais API",
                    Version = "v1"
                }
                );
        });


        //var userManager = (UserManager<ibasis_apiUser>)Scope.ServiceProvider.GetService(typeof(UserManager<ibasis_apiUser>));
        //UserManager<ibasis_apiUser> userManager =serviceProvider.GetRequiredService<UserManager<ibasis_apiUser>>();
        //UserManager<ibasis_apiUser> userManager = new UserManager<ibasis_apiUser>(); services.G serviceProvider.GetRequiredService<UserManager<ibasis_apiUser>>();


        services.AddSingleton<ITokenRefresher>(x =>
                new TokenRefresher(key, 
                x.GetService<IJWTAuthenticationManager>(),
                x.GetService<UserManager<ibasis_apiUser>>() //Suspect Line
                ));

        services.AddSingleton<IRefreshTokenGenerator, RefreshTokenGenerator>();

        services.AddSingleton<IJWTAuthenticationManager>(x =>
                new JWTAuthenticationManager(tokenKey, 
                x.GetService<IRefreshTokenGenerator>(),
                x.GetService<UserManager<ibasis_apiUser>>()//Suspect Line
            ));
    }

    // This method gets called by the runtime. Use this method to configure the HTTP request pipeline.
    public void Configure(IApplicationBuilder app, IWebHostEnvironment env, ILoggerFactory loggerFactory)
    {
        if (env.IsDevelopment())
        {
            app.UseDeveloperExceptionPage();
        }

        app.UseHttpsRedirection();

        app.UseRouting();

        app.UseAuthentication();
        app.UseAuthorization();

        app.UseSwagger();
        app.UseSwaggerUI(x =>
        {
            x.SwaggerEndpoint("/swagger/v1/swagger.json", "iBasis API v1");
            //x.RoutePrefix = string.Empty;
        });
        loggerFactory.AddFile(Configuration.GetSection("Logging"));
        app.UseEndpoints(endpoints =>
        {
            endpoints.MapControllers();
        });
    }
}

JWTAuthenticationManager界面

 public interface IJWTAuthenticationManager
{
    Task<AuthenticationResponse> Authenticate(string username, string password);
    Task<AuthenticationResponse> Authenticate(string username, Claim[] claims);
}

JWTAuthenticationManager

public class JWTAuthenticationManager : IJWTAuthenticationManager
{
   

    private readonly string tokenKey;
    private readonly IRefreshTokenGenerator refreshTokenGenerator;
    private readonly UserManager<ibasis_apiUser> _userManager;

    public JWTAuthenticationManager(string tokenKey, 
        IRefreshTokenGenerator refreshTokenGenerator,
        UserManager<ibasis_apiUser> usermanager)
    {
        this.tokenKey = tokenKey;
        this.refreshTokenGenerator = refreshTokenGenerator;
        _userManager = usermanager;
    }


    public async Task<AuthenticationResponse> Authenticate(string username, Claim[] claims)
    {
        var token = GenerateTokenString(username, DateTime.UtcNow, claims);
        var refreshToken = refreshTokenGenerator.GenerateToken();

        var user = await _userManager.FindByNameAsync(username);
        user.RefreshTokens = refreshToken;
        await _userManager.UpdateAsync(user);

        return new AuthenticationResponse
        {
            JwtToken = token,
            RefreshToken = refreshToken
        };
    }


    public async Task<AuthenticationResponse> Authenticate(string username, string password)
    {
        var user = await _userManager.FindByNameAsync(username);

        if(user != null && !await _userManager.CheckPasswordAsync(user, password))
        {
            return null;
        }

        var token = GenerateTokenString(username, DateTime.UtcNow);
        var refreshToken = refreshTokenGenerator.GenerateToken();

        //Save Token
        user.RefreshTokens = refreshToken;
        await _userManager.UpdateAsync(user);

        return new AuthenticationResponse
        {
            JwtToken = token,
            RefreshToken = refreshToken
        };
    }

    string GenerateTokenString(string username, DateTime expires, Claim[] claims = null)
    {
        var tokenHandler = new JwtSecurityTokenHandler();
        var key = Encoding.ASCII.GetBytes(tokenKey);
        var tokenDescriptor = new SecurityTokenDescriptor
        {
            Subject = new ClaimsIdentity(
             claims ?? new Claim[]
            {
                new Claim(ClaimTypes.Name, username)
            }),
            //NotBefore = expires,
            Expires = expires.AddMinutes(2),
            SigningCredentials = new SigningCredentials(
                new SymmetricSecurityKey(key),
                SecurityAlgorithms.HmacSha256Signature)
        };

        return tokenHandler.WriteToken(tokenHandler.CreateToken(tokenDescriptor));
    }

    private async Task<bool> IsValidUsernameAndPassowrd(string username, string password)
    {
        var user = await _userManager.FindByNameAsync(username);
        return await _userManager.CheckPasswordAsync(user, password);

    }
}

TokenRefresher

 public class TokenRefresher : ITokenRefresher
{
    private readonly byte[] key;
    private readonly IJWTAuthenticationManager jWTAuthenticationManager;
    private readonly UserManager<ibasis_apiUser> _userManager;

    public TokenRefresher(byte[] key, IJWTAuthenticationManager jWTAuthenticationManager,
        UserManager<ibasis_apiUser> usermanager)
    {
        this.key = key;
        this.jWTAuthenticationManager = jWTAuthenticationManager;
        _userManager = usermanager;
    }

    public async Task<AuthenticationResponse> Refresh(RefreshCred refreshCred)
    {
        var tokenHandler = new JwtSecurityTokenHandler();
        SecurityToken validatedToken;
        var pricipal = tokenHandler.ValidateToken(refreshCred.JwtToken,
            new TokenValidationParameters
            {
                ValidateIssuerSigningKey = true,
                IssuerSigningKey = new SymmetricSecurityKey(key),
                ValidateIssuer = false,
                ValidateAudience = false,
                ValidateLifetime = false //here we are saying that we don't care about the token's expiration date
            }, out validatedToken);
        var jwtToken = validatedToken as JwtSecurityToken;
        if (jwtToken == null || !jwtToken.Header.Alg.Equals(SecurityAlgorithms.HmacSha256, StringComparison.InvariantCultureIgnoreCase))
        {
            throw new SecurityTokenException("Invalid token passed!");
        }

        var userName = pricipal.Identity.Name;
        var user = await _userManager.FindByNameAsync(userName);

        if (refreshCred.RefreshToken != user.RefreshTokens)
        {
            throw new SecurityTokenException("Invalid token passed!");
        }

        return  await jWTAuthenticationManager.Authenticate(userName, pricipal.Claims.ToArray());
    }

    
}

1 个答案:

答案 0 :(得分:1)

因此,我用一些代码示例扩展了我的评论

首先,考虑将IJWTAuthenticationManager注册为范围服务:

services.AddScoped<IJWTAuthenticationManager>(serviceProvider =>
    ActivatorUtilities.CreateInstance<JWTAuthenticationManager>(serviceProvider, tokenKey));

您可以使用ActivatorUtilities类将tokenKey作为自定义参数传递,同时由DI容器填充其他参数。

如果IJWTAuthenticationManager必须是单身,请尝试注入IHttpContextAccessor

services.AddHttpContextAccessor();
services.AddSingleton<IJWTAuthenticationManager>(serviceProvider =>
    ActivatorUtilities.CreateInstance<JWTAuthenticationManager>(serviceProvider, tokenKey));

然后您的JWTAuthenticationManager类应如下所示:

public class JWTAuthenticationManager : IJWTAuthenticationManager
{
    private readonly string tokenKey;
    private readonly IRefreshTokenGenerator refreshTokenGenerator;
    private readonly IHttpContextAccessor _httpContextAccessor;

    public JWTAuthenticationManager(
        string tokenKey, 
        IRefreshTokenGenerator refreshTokenGenerator,
        IHttpContextAccessor httpContextAccessor)
    {
        this.tokenKey = tokenKey;
        this.refreshTokenGenerator = refreshTokenGenerator;
        _httpContextAccessor = httpContextAccessor;
    }

    public async Task<AuthenticationResponse> Authenticate(string username, Claim[] claims)
    {
        // Resolve UserManager for current request
        var serviceProvider = _httpContextAccessor.HttpContext.RequestServices;
        var userManager = serviceProvider.GetRequiredService<UserManager<ibasis_apiUser>>();
        
        // The rest of your method
    }
}