添加服务时出现错误。启动类中的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());
}
}
答案 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
}
}