Как мне использовать свой собственный провайдер аутентификации ServiceStack с Redis?

Я реализовал кастом CredentialsAuthProvider для моей аутентификации и использовал его по умолчанию в памяти сессии хранения.

Теперь я попытался изменить хранилище сеанса на Redis и добавил это в свой Configure() метод в AppHost:

container.Register<IRedisClientsManager>(c => 
    new PooledRedisClientManager("localhost:6379"));

container.Register<ICacheClient>(c => (ICacheClient)c
    .Resolve<IRedisClientsManager>()
    .GetCacheClient()).ReusedWithin(Funq.ReuseScope.None);

Теперь, когда я аутентифицируюсь, я вижу, что ключ с urn:iauthsession:... добавлен на мой сервер Redis. Но все маршруты с [Authenticate] атрибут дать 401 Unauthorized ошибка.

CustomCredentialsAuthProvider реализован так:

public class CustomCredentialsAuthProvider : CredentialsAuthProvider
{
    public override bool TryAuthenticate(IServiceBase authService, string userName, string password)
    {
        if (userName != string.Empty && password != string.Empty)
        {
            // Database call ...
            var session = (CustomSession)authService.GetSession();
            session.ClientId = login.ClientId;
            // Fill session...
            authService.SaveSession(session, SessionExpiry);
            return true;
        }
        return false;
    }
}

Версия ServiceStack: 3.9.71

РЕДАКТИРОВАТЬ:

Я пытался переопределить CredentialsAuthProviderIsAuthorized метод, но без успеха.

Но я наследую свой сеансовый объект от AuthUserSessionкоторый также имеет IsAuthorized метод. Когда я возвращаю true из этого метода, сеанс Redis работает с Authenticate Атрибут.

public class CustomSession : AuthUserSession
{
    public int ClientId { get; set; }
    ...

    public override bool IsAuthorized(string provider)
    {
        return true;
    }
}

2 ответа

Решение

Я не мог найти способ получить [Authenticate] Атрибут для работы с хранилищем Redis.

Я должен был написать обычай [SessionAuth] атрибут

public class SessionAuthAttribute : RequestFilterAttribute
{
    public ICacheClient cache { get; set; }
    public string HtmlRedirect { get; set; }

    public SessionAuthAttribute()
    {
    }

    public override void Execute(IHttpRequest req, IHttpResponse res, object requestDto)
    {
        string sessionId = req.GetSessionId();
        if (string.IsNullOrEmpty(sessionId))
        {
            HandleNoSession(req, res);

        }
        else
        {
            var session = cache.Get<CustomSession>("urn:iauthsession:" + sessionId);
            if (session == null || !session.IsAuthenticated)
            {

                HandleNoSession(req, res);
            }
        }
    }

    private void HandleNoSession(IHttpRequest req, IHttpResponse res)
    {

        if (req.ResponseContentType.MatchesContentType(MimeTypes.Html))
        {

            res.RedirectToUrl(HtmlRedirect);
            res.End();

        }
        res.StatusCode = (int)HttpStatusCode.Unauthorized;
        res.Write("not authorized");
        res.Close();
    }
}

В моем методе AppHost Configure() я просто регистрирую SessionFeature и IRedisClientsManager/ICacheClient:

Plugins.Add(new SessionFeature());

container.Register<IRedisClientsManager>(c => new PooledRedisClientManager("localhost:6379"));

container.Register<ICacheClient>(c => (ICacheClient)c.Resolve<IRedisClientsManager>()
        .GetCacheClient()).ReusedWithin(Funq.ReuseScope.None);

Класс CustomSession наследуется от AuthUserSession:

public class CustomSession : AuthUserSession
{
    public int ClientId { get; set; }
    ...
}

И у меня есть обычный сервисный маршрут в /login/auth для части аутентификации и /login/logout route для удаления сеанса:

public class LoginService : Service
{
    public ICacheClient cache { get; set; }

    public object Post(AuthRequest request)
    {
        string userName = request.UserName;
        string password = request.Password;

        // check login allowed

        if (IsAllowed)
        {

            var session = SessionFeature.GetOrCreateSession<CustomSession>(cache);

            session.ClientId = login.ClientId;
            ...
            session.IsAuthenticated = true;
            session.Id = SessionFeature.GetSessionId();

            this.SaveSession(session, TimeSpan.FromSeconds(30 * 60));


            return true;
        }

        return false;
    }


    [SessionAuth]
    public object Any(LogoutRequest request)
    {
        this.RemoveSession();
        return true;
    }
}

}

Я все еще заинтересован в решении, которое работает с нормальным [Authenticate] Атрибут.

Authenticate Атрибут вызывает IsAuthorized из AuthUserSession учебный класс. В моем случае, чтобы он работал с клиентом Redis cache, я сделал следующее

public override bool IsAuthorized(string provider)
{
    string sessionKey = SessionFeature.GetSessionKey(this.Id);
    ICacheClient cacheClient = AppHostBase.Resolve<ICacheClient>();

    CustomUserSession session = cacheClient.Get<CustomUserSession>(sessionKey);

    if (session == null)
    {
        return false;
    }

    return session.IsAuthenticated;
}
Другие вопросы по тегам