- c - 在位数组中找到第一个零
- linux - Unix 显示有关匹配两种模式之一的文件的信息
- 正则表达式替换多个文件
- linux - 隐藏来自 xtrace 的命令
我被卡住了,我似乎无法弄清楚这一点。我有一个带有接口(interface)的简单类。我正在将 EFContext 和 Logger 注入(inject)到该服务中。出于某种原因,无论我如何注册服务,它始终是单例。我将 Guid 属性放在类上以查看它是否根据每个请求更改,但它保持不变。
这是 AccountService 类及其接口(interface):
public interface IAccountService
{
Account GetAccountByEmailAndPassword(string emailAddress, string password);
}
public class AccountService : IAccountService
{
private readonly IEFContext _context;
private readonly ILogger<AccountService> _logger;
private string _guid;
public AccountService()
{
_context = context;
_logger = logger;
_guid = Guid.NewGuid().ToString();
}
public Account GetAccountByEmailAndPassword(string emailAddress, string password)
{
try
{
//get the account
var account = _context.Account.FirstOrDefault(x => x.EmailAddress == emailAddress);
//make sure we have an account
if (account == null)
return null;
//generate hash from account
var accountHash = GeneratePasswordSaltHash(account.Password, account.PasswordSalt);
//generate hash from credentials passed in
var passedInHash = GeneratePasswordSaltHash(
Convert.ToBase64String(HashPassword(password)),
account.PasswordSalt);
// TODO: number of failed attempts should lock account etc.
return accountHash == passedInHash ? account : null;
} catch (Exception ex)
{
_logger.LogError("Exception in AccountService: " + ex.ToString());
throw;
}
}
}
这是我注册服务的方式:
public void ConfigureServices(IServiceCollection services)
{
// App Settings
services.Configure<AppSettings>(Configuration.GetSection("AppSettings"));
// Add DBContext
var connectionString = Configuration["AppSettings:Data:ConnectionString"];
services.AddDbContext<EFContext>(options => options.UseSqlServer(connectionString));
// Add framework services.
services.AddMvc();
// Add DI
services.AddScoped<IEFContext, EFContext>();
services.AddScoped<IAccountService, AccountService>();
}
这是 EFContext 类及其接口(interface):
public interface IEFContext
{
DbSet<Account> Account { get; set; }
int SaveChanges();
EntityEntry Update(object entity);
}
public class EFContext : DbContext, IEFContext
{
public EFContext(DbContextOptions options) : base(options) {}
public DbSet<Account> Account { get; set; }
}
我可以使用上下文访问数据库和所有这些,但是,一切都是单例。我首先被提醒注意这个问题,因为如果我进入数据库并手动更新帐户上的一些数据,然后在代码中再次请求该帐户,数据就会过时。我认为这是一个上下文问题,但我认为我正在使用 .AddScoped<>
正确配置上下文生命周期,但我无法让它工作。然后我尝试添加 _guid
属性(property)给AccountService
以确定它是否在每个请求上都更新了,但似乎没有。我试过.AddTransient<>
以及。任何帮助表示赞赏。非常感谢。
编辑这是我的配置方法:
public void Configure(IApplicationBuilder app, IHostingEnvironment env, ILoggerFactory loggerFactory)
{
loggerFactory.AddConsole(Configuration.GetSection("Logging"));
loggerFactory.AddDebug();
loggerFactory.AddSerilog();
//Token stuff
// secretKey contains a secret passphrase only your server knows
var secretKey = "mysupersecret_secretkey!123";
var signingKey = new SymmetricSecurityKey(Encoding.ASCII.GetBytes(secretKey));
var tokenValidationParameters = new TokenValidationParameters
{
// The signing key must match!
ValidateIssuerSigningKey = true,
IssuerSigningKey = signingKey,
// Validate the JWT Issuer (iss) claim
ValidateIssuer = true,
ValidIssuer = "ExampleIssuer",
// Validate the JWT Audience (aud) claim
ValidateAudience = true,
ValidAudience = "ExampleAudience",
// Validate the token expiry
ValidateLifetime = true,
// If you want to allow a certain amount of clock drift, set that here:
ClockSkew = TimeSpan.Zero
};
app.UseJwtBearerAuthentication(new JwtBearerOptions
{
AutomaticAuthenticate = true,
AutomaticChallenge = true,
TokenValidationParameters = tokenValidationParameters
});
// Token generator
var options = new TokenProviderOptions
{
Audience = "ExampleAudience",
Issuer = "ExampleIssuer",
SigningCredentials = new SigningCredentials(signingKey, SecurityAlgorithms.HmacSha256),
};
app.UseMiddleware<TokenProviderMiddleware>(Options.Create(options));
app.UseMvc();
}
在我的 token 中间件中,我确实使用了 AccountService,这是 token 中间件:
public class TokenProviderMiddleware
{
private readonly RequestDelegate _next;
private readonly TokenProviderOptions _options;
private readonly IAccountService _accountService;
public TokenProviderMiddleware(RequestDelegate next, IOptions<TokenProviderOptions> options, IAccountService accountService)
{
_next = next;
_options = options.Value;
_accountService = accountService;
}
public Task Invoke(HttpContext context)
{
// If the request path doesn't match, skip
if (!context.Request.Path.Equals(_options.Path, StringComparison.Ordinal))
{
return _next(context);
}
if (!context.Request.Method.Equals("POST")
|| !context.Request.ContentType.Contains("application/json"))
{
context.Response.StatusCode = 400;
return context.Response.WriteAsync("Bad request.");
}
return GenerateToken(context);
}
private async Task GenerateToken(HttpContext context)
{
var rawAccount = await new StreamReader(context.Request.Body).ReadToEndAsync();
var authAccount = JsonConvert.DeserializeObject<AuthAccount>(rawAccount);
var account = _accountService.GetAccountByEmailAndPassword(authAccount.EmailAddress, authAccount.Password);
if (account == null)
{
context.Response.StatusCode = 400;
await context.Response.WriteAsync("Invalid email address or password.");
return;
}
var now = DateTime.UtcNow;
// Specifically add the jti (random nonce), iat (issued timestamp), and sub (subject/user) claims.
// You can add other claims here, if you want:
var claims = new Claim[]
{
new Claim(JwtRegisteredClaimNames.Sub, account.EmailAddress),
new Claim(JwtRegisteredClaimNames.Jti, Guid.NewGuid().ToString()),
new Claim(JwtRegisteredClaimNames.Iat, ((DateTimeOffset)now).ToUnixTimeSeconds().ToString(), ClaimValueTypes.Integer64),
new Claim(ClaimTypes.Role, account.RoleId.ToString()),
new Claim(ClaimTypes.Name, account.EmailAddress)
};
// Create the JWT and write it to a string
var jwt = new JwtSecurityToken(
issuer: _options.Issuer,
audience: _options.Audience,
claims: claims,
notBefore: now,
expires: now.Add(_options.Expiration),
signingCredentials: _options.SigningCredentials);
var encodedJwt = new JwtSecurityTokenHandler().WriteToken(jwt);
var response = new ApiResponse<AuthAccount>
{
StatusCode = (int)HttpStatusCode.OK,
Message = "Access granted",
Data = new AuthAccount
{
Access_Token = encodedJwt,
Expires_In = (int)_options.Expiration.TotalSeconds
}
};
// Serialize and return the response
context.Response.ContentType = "application/json";
await context.Response.WriteAsync(JsonConvert.SerializeObject(response, new JsonSerializerSettings { Formatting = Formatting.Indented }));
}
}
最佳答案
中间件只被实例化一次,所以它实际上是一个单例。
因此,您注入(inject)中间件构造函数的所有内容都从单例容器(您可以通过 Configure 方法中的 app.ApplicationServices
访问的容器)解析。
我看到您的 IAccountService
被注入(inject)到中间件中,所以这似乎是导致问题的原因。您必须在 Invoke 方法的每个上下文基础上使用
public Task Invoke(HttpContext context, IAccountService accountService)
{
// If the request path doesn't match, skip
if (!context.Request.Path.Equals(_options.Path, StringComparison.Ordinal))
{
return _next(context);
}
if (!context.Request.Method.Equals("POST")
|| !context.Request.ContentType.Contains("application/json"))
{
context.Response.StatusCode = 400;
return context.Response.WriteAsync("Bad request.");
}
return GenerateToken(context, accountService);
}
或
public Task Invoke(HttpContext context)
{
var accountService = context.RequestServices.GetRequiredService<IAccountService>();
// If the request path doesn't match, skip
if (!context.Request.Path.Equals(_options.Path, StringComparison.Ordinal))
{
return _next(context);
}
if (!context.Request.Method.Equals("POST")
|| !context.Request.ContentType.Contains("application/json"))
{
context.Response.StatusCode = 400;
return context.Response.WriteAsync("Bad request.");
}
return GenerateToken(context, accountService);
}
关于c# - asp.net core DI 只注入(inject)单例,作用域和 transient 不工作,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/41856278/
我最近购买了《C 编程语言》并尝试了 Ex 1-8这是代码 #include #include #include /* * */ int main() { int nl,nt,nb;
早上好!我有一个变量“var”,可能为 0。我检查该变量是否为空,如果不是,我将该变量保存在 php session 中,然后调用另一个页面。在这个新页面中,我检查我创建的 session 是否为空,
我正在努力完成 Learn Python the Hard Way ex.25,但我无法理解某些事情。这是脚本: def break_words(stuff): """this functio
我是一名优秀的程序员,十分优秀!