Набор услуг Отличается безопасностью на основе маршрутов

#servicestack

Вопрос:

У нас есть хост ServiceStack, в котором мы модульно распределили службы. Кроме того, у нас есть пользовательское решение для аутентификации, основанное на базовой аутентификации. Но что мы хотели бы сделать, так это иметь разные методы аутентификации для разных служб, возможно, основанные на маршрутах? Возможно ли это?

Во-вторых, можно ли назначить общий префикс маршрута на основе сервиса? Как я уже сказал, мы модулировали наши службы, и в определении хоста мы вводим сборки различных служб, но можно ли изменить префикс маршрута, т. е. Service1 на localhost/api1/servicemethods, Service2 на localhost/api2/servicemethods и т. Д.?

Ответ №1:

Вы можете ограничить, что Служба должна проходить проверку подлинности только у определенного поставщика, указав имя поставщика в [Authenticate] атрибуте, например:

 [Authenticate(AuthenticateService.ApiKeyProvider)]
public class ApiKeyAuthServices : Service
{
    public object Any(ApiKeyOnly request) => ...;
}

[Authenticate(AuthenticateService.JwtProvider)]
public class JwtAuthServices : Service
{
    public object Any(JwtOnly request) => ...;
}
 

В противном случае внутри вашей службы вы можете проверить, как был аутентифицирован запрос, посмотрев на base.SessionAs<AuthUserSession>().AuthProvider .

Для определения динамических маршрутов взгляните на:

Хотя ServiceStack не предназначен для определения разных наборов приложений в одном и том же хосте приложений, поэтому, если вы пытаетесь это сделать, я бы рекомендовал вместо этого иметь разные хосты приложений и использовать шлюз службы для любой связи между службами.

Комментарии:

1. Большое спасибо за это. Это выглядит именно так, как мне нужно, однако у меня есть несколько проблем с реализацией, с которыми, я надеюсь, вы сможете помочь.

2. У меня есть два пользовательских поставщика аутентификации, оба основаны на базовом поставщике авторизации, но проверяются внутри по-разному. В AppHost я зарегистрировал обоих поставщиков, и, похоже, это сработало правильно. Теперь я пытаюсь ограничить использование службы для использования определенного поставщика аутентификации. В блоке [Аутентификация] я использую Provider= и пытаюсь ввести строковое имя, как, по-видимому, требуется.

3. Я попытался использовать класс ProviderClass. Свойство Name, но оно недовольно тем, что оно является статической строкой, а не константой. Я попытался вручную задать свойства имен двух поставщиков аутентификации разными, но они переписали друг друга. Поэтому я создаю значение для переопределения свойства Name в базовом классе. Но все равно результат не радует. Я попытался ввести строку const с именем, которое я задал в конструкторе AuthProviders, она принимает это, но когда я пытаюсь вызвать метод, я получаю предупреждение: поставщик аутентификации с таким именем не найден. Не могли бы вы, пожалуйста, сказать мне, что я делаю не так.

4. Атрибуты метаданных @MarkLFT встроены . библиотеки dll, поэтому они могут быть константами времени компиляции, вы можете использовать AuthenticateService.BasicProvider или просто "basic" строковый литерал. Чтобы помочь в тестировании, вы можете использовать AuthenticateService.GetAuthProviders() для разрешения всех зарегистрированных поставщиков и AuthenticateService.GetAuthProviders(provider) фильтрации, где IAuthProvider.Provider == provider

Ответ №2:

Большое спасибо за ваш ответ. Должно быть, я делаю что-то в корне неправильное, хотя я зарегистрировал два пользовательских поставщика авторизации, оба на основе базового поставщика авторизации, используя AuthenticateService.Функция GetAuthProviders() возвращает пустой массив.

Это код, который я использую для регистрации поставщиков авторизации, и оба они позволяют мне войти в систему, поэтому я знаю, что они работают.

 Plugins.Add(new AuthFeature(() => new CustomUserSession(),
                new IAuthProvider[] {
                    new RMCredentialsAuthProvider(),
                    new RMKOTAuthProvider()
                }));
 

Код от одного из пользовательских поставщиков является

 public class RMKOTAuthProvider : BasicAuthProvider
{
    #region Public Constructors

    public RMKOTAuthProvider() : base()
    {
        
    }

    #endregion Public Constructors

    #region Public Methods

    public override Task<IHttpResult> OnAuthenticatedAsync(IServiceBase authService, IAuthSession session, IAuthTokens tokens, Dictionary<string, string> authInfo, CancellationToken token = default)
    {
        session.FirstName = session.UserAuthName;
        session.Roles = new List<string>
        {
            "KOT"
        };

        authService.SaveSessionAsync(session, SessionExpiry);

        return base.OnAuthenticatedAsync(authService, session, tokens, authInfo, token);
    }

    public override Task<bool> TryAuthenticateAsync(IServiceBase authService, string userName, string password, CancellationToken token = default)
    {
        try
        {
            if (userName.IsNullOrEmpty() || password.IsNullOrEmpty())
                return Task.FromResult(false);

            var result = VerifyUser(username, password);

            return Task.FromResult(result);
            
        }
        catch (InvalidCastException)
        {
            return Task.FromResult(false);
        }
    }

    #endregion Public Methods
}
 

Не могли бы вы объяснить, пожалуйста, какой шаг я пропускаю, чтобы GetAuthProviders() мог перечислить поставщиков, и я мог использовать метаданные, которые вы описали ранее.

Заранее большое спасибо за вашу помощь в этом.

Комментарии:

1. Я нашел, как получить поставщиков с помощью GetProviders (), мне нужно было изменить порядок в методе AppHost.Configure. Но теперь у меня есть странная вещь, в пользовательском интерфейсе swagger я использую плагин OpenAPI. когда я нажимаю «Разрешить», он запрашивает ключ API, а не имя пользователя и пароль. Оба наших поставщика услуг аутентификации основаны на Базовом поставщике, поэтому он должен запрашивать пароль. У нас нет поставщиков ApiKey или настроек где-либо в API. Является ли это ошибкой в плагине OpenAPI, потому что мы используем пользовательских поставщиков, или я делаю что-то не так?

2. В дополнение к тому, что swagger не использует правильный тип аутентификации, при использовании метода JsonServiceClient и SetCredentials я получаю сообщение об ошибке «Для поставщика OAuth ‘basic’не была добавлена конфигурация». Похоже, что при использовании пользовательских поставщиков аутентификации и изменении значения поставщика для использования в метаданных аутентификации он больше не знает, как управлять процессом входа в систему.

3. Примечание.Вы должны обновить свой вопрос, а не добавлять новый вопрос в качестве ответа. В связи с этим у вас не должно быть более 1 поставщика услуг аутентификации, использующего одно и то же имя поставщика или реализующего одного и того же поставщика проверки подлинности, например HTTP Basic Auth, который будет конфликтовать при попытке проверки подлинности одного и того же запроса HTTP Basic Auth. Чтобы указать уникальное имя, вызовите базовый конструктор (IAppSettings, область, поставщик) или, если у вас только 1, вы можете использовать basic имя поставщика и /auth/basic область по умолчанию в конструкторе по умолчанию.

4. Извините, я думал, что все это связано, вот почему я добавил это в этот разговор. Я решил свою первоначальную проблему, создав только одного пользовательского поставщика, а затем в нем используя Запрос. Dto для поиска пункта назначения и применения правильной проверки подлинности. Это кажется очень грязным, но это работает. Могу ли я предложить изменение, чтобы метаданные могли использовать свойство Name вместо поставщика, возможно, тогда мы могли бы использовать несколько поставщиков с одним и тем же базовым типом. Это позволило бы создать более чистый код. Я не знаю, будет ли это полезно и для других?

5. Вы добавили вопрос в качестве ответа, который в конечном итоге будет удален всякий раз, когда мод его увидит, вам нужно будет либо обновить существующий вопрос, либо задать новый. Не уверен, на какое Name свойство вы ссылаетесь, единственное Name свойство BasicAuthProvider является статическим, поэтому его нельзя использовать напрямую, но оно используется в качестве значения по умолчанию для его Provider имени.