Dependency Injection создает экземпляры разных типов

97
05 января 2022, 13:50

У меня есть сервис по работе с JWT

public class JwtService : IJwtService
{
    private IDistributedCache cache;
    private IHttpContextAccessor httpContext;
    private IOptions<JwtOptions> jwtOptions;
    private MyContext context;
    private ILogger<JwtService> logger;
    public JwtService(MyContext context, IDistributedCache cache,
        IHttpContextAccessor httpContext, IOptions<JwtOptions> jwtOptions, ILogger<JwtService> logger)
    {
        this.cache = cache;
        this.httpContext = httpContext;
        this.jwtOptions = jwtOptions;
        this.context = context;
        this.logger = logger;
    }
    private string GetCurrentJwtString()
    {            
        StringValues header = httpContext.HttpContext.Request.Headers["authorization"];            
        return StringValues.IsNullOrEmpty(header) ? string.Empty : header.Single().Split(" ").Last();
    }
    public async Task<bool> IsActiveCurrentJwtAsync()
    {
        return await IsActiveJwtAsync(GetCurrentJwtString());
    }
    public async Task DeactivateCurrentJwtAsync()
    {            
        await DeactivateJwtAsync(GetCurrentJwtString());
    }
    public async Task DeactivateJwtAsync(string token)
    {                      
        await cache.SetStringAsync(token, "diactivated", new DistributedCacheEntryOptions()
        {
            AbsoluteExpirationRelativeToNow = TimeSpan.FromMinutes(jwtOptions.Value.ExpiryMinutes)
        });                           
    }               
    public async Task<bool> IsActiveJwtAsync(string token)
    {                              
        return await cache.GetStringAsync(token) == null;
    }   

И есть два места в проекте, которые взаимодействуют с этим сервисом:

Первое. В контроллере пользователя при выходе из аккаунта я вызываю метод деактивации токена, который просто заносит токен в кэш.

[Authorize]
[Route("api/[controller]/[action]")]
[ApiController]
public class UserController : Controller
{
    IJwtService jwtServ;
    IUserService userServ;
    public UserController(IJwtService jwtServ, IUserService userServ)
    {
        this.jwtServ = jwtServ;
        this.userServ = userServ;
    }        
    [ActionName("Logout")]
    public async Task Logout()
    {
        await jwtServ.DeactivateCurrentJwtAsync();
    }
}

И второе: В классе Startup в настройке аутентификации после валидации токена я проверяю, есть ли он в кэше или нет.

services.AddDistributedRedisCache(option =>
        {
            option.Configuration = Configuration["Redis:Address"];
            option.InstanceName = "maelstorm";
        });
services
        .AddAuthentication(options => {
            options.DefaultAuthenticateScheme = jwtSchemeName;
            options.DefaultChallengeScheme = jwtSchemeName;                                           
        })
        .AddJwtBearer(jwtSchemeName, jwtBearerOptions => {                    
            jwtBearerOptions.TokenValidationParameters = new TokenValidationParameters
            {                        
                ValidateIssuerSigningKey = true,
                IssuerSigningKey = signingDecodingKey.GetKey(),
                TokenDecryptionKey = encryptingDecodingKey.GetKey(),
                ValidateIssuer = true,
                ValidIssuer = Configuration["Jwt:Issuer"],
                ValidateAudience = true,
                ValidAudience = Configuration["Jwt:Audience"],
                ValidateLifetime = true,
                ClockSkew = TimeSpan.FromSeconds(5)                        
            };                    
            var servProvider = services.BuildServiceProvider();
            var jwtService = servProvider.GetService<IJwtService>();
            jwtBearerOptions.Events = new JwtBearerEvents
            {                            
                OnAuthenticationFailed = context =>
                {                            
                    if (context.Exception.GetType() == typeof(SecurityTokenExpiredException))
                    {
                        context.Response.Headers.Add("Token-Expired", "true");
                    }                                                        
                    return Task.CompletedTask;                            
                },
                OnTokenValidated = async context =>
                {                            
                    if(! await jwtService.IsActiveCurrentJwtAsync())
                    {
                        context.Fail("Token is not active");                                                                                      
                    }                                    
                }                                                                       
            };

Проблема заключается в том, что в первом случае переменная cache в JWTService имеет тип Microsoft.Extensions.Caching.Distributed.MemoryDistributedCache,а во втором - Microsoft.Extensions.Caching.Redis.RedisCachе. Получается, что метод деактивации токена записывает токен в один кэш, а метод проверки пытается достать из другого и поэтому ничего не работает. Почему механизм внедрения зависимостей создает экземпляры разных типов? Каким образом сделать так, чтобы всегда использовался лишь Redis в качестве кэша?

Answer 1

Механизм внедрения зависимостей создаёт разные зависимости потому что у вас два разных ServiceProvider: один был создан фреймворком, а другой создали вы сами:

            // Не делайте так!
            var servProvider = services.BuildServiceProvider();
            var jwtService = servProvider.GetService<IJwtService>();

Вместо того, чтобы вызывать случайные методы, вам надо получить доступ к системному ServiceProvider. Например, вот так:

                OnTokenValidated = async context =>
                {
                    var jwtService = context.HttpContext.RequestServices.GetService<IJwtService>();
                    // ...
                }
READ ALSO
DataGridView извлечение информации

DataGridView извлечение информации

Курил форумы и встал вопрос :

107
Ожидание ввода текста без остановки программы

Ожидание ввода текста без остановки программы

В моей программе есть вкладка, где я могу написать и запустить кодПри его запуске, появляется окошко с двумя TextBox'ами, в первом вывод, а во втором...

72
Поможете дописать авто-редирект при успешной оплате?

Поможете дописать авто-редирект при успешной оплате?

В данный момент при любом статусе оплаты или ее стадии осуществляется редирект на нужную страницу и все ок, но только при клике на "вернуться...

190
Как можно обезопасить метод удаления данных?

Как можно обезопасить метод удаления данных?

Есть следующий код, задача которого удалить данные из бдСейчас все данные удаляются при переходе по ссылке localhost/?id=666

236