Как декодировать и проверить настроенный JWT с сервера авторизации Spring на Spring Resource Server?

Я использую сервер авторизации Spring 1.0.0. В этом я настроил JWT в соответствии с моим требованием следующим образом. Допустим, есть пользователь «vinay». И его роль тоже «винай».

Я добавляю дополнительное поле «авторитет»: [{"role":"ROLE_vinay"}] в JWT. Ниже представлена ​​полезная нагрузка JWT.

{ "sub": "vinay", "aud": "messaging-client", "nbf": 1671430162, "authority": [ { "role": "ROLE_vinay" } ], "scope": [ "openid" ], "iss": "http://localhost:8000", "exp": 1671430462, "iat": 1671430162 }

Для роли "vinay" существует несколько разрешений/ограничений. Эти данные присутствуют в MySQL. После аутентификации пользователя (логина) я добавляю эти данные в Redis в пару KEY:VALUE, где ключ — «vinay», значение — [Ограничение 1, Разрешение 1, Разрешение 2].

"vinay" : [Permission 1, Permission 2, Restriction 1]

После успешного входа в систему от пользователя (vinay) клиент получает пользовательский токен и отправляет запрос на сервер ресурсов Spring с пользовательским JWT в заголовке с префиксом Bearer. Для каждого запроса. Как декодировать входящий JWT, получить «роль» и проверить ее от Redis? Если в Redis есть какие-то ограничения, то он не должен их разрешать. Он должен выдать «401 неавторизованный». Если есть разрешение, то он должен продолжить запрос.

что ты уже испробовал?

Steve Riesenberg 05.01.2023 17:09

Привет @SteveRiesenberg, я могу проверить и проверить токен на сервере ресурсов. Я представил свой собственный «JwtDecoder», который имеет реализацию класса «OAuth2TokenValidator», которая выполняет пользовательскую проверку. Он проходит, но у меня все еще есть некоторые сомнения. 1) Что происходит, когда Сервер авторизации меняет пару ключей? Нужно ли перезапускать сервер ресурсов или в любом случае уведомлять сервер ресурсов? 2) Как получить информацию о входящем запросе в «JwtDecoder» или «OAuth2TokenValidator»? Я использую «(ServletRequestAttributes) RequestContextHolder». Это правильно или неправильно? Спасибо.

Vinayak Pattar 06.01.2023 05:56

Это не очень целенаправленный вопрос, похоже, у вас довольно много вопросов. Многое из того, о чем вы спрашиваете, может быть обнаружено путем тестирования и/или чтения документации. Что касается получения сведений о запросе, я бы сказал, что это, вероятно, неправильно, потому что JwtDecoder не там, где происходит обработка на уровне запроса. Он просто проверяет/декодирует токен. Это зависит от того, что вы хотите делать с деталями запроса, где правильно разместить эту логику.

Steve Riesenberg 06.01.2023 20:29

Мне также интересно, почему вы хотите добавить зависимость к Redis для проверки JWT, когда вместо этого вы можете включить разрешения/ограничения в JWT при его создании. Вы ожидаете много таких записей? Наконец, я не понимаю, зачем вам проверять Redis, если изначально данные уже присутствуют в MySQL. Я бы подумал, что использование MySQL для добавления разрешений/ограничений в JWT через OAuth2TokenCustomizer<JwtEncodingContext> было бы подходящим способом, а не использование Redis вообще. Очевидно, мне не хватает части вашего варианта использования.

Steve Riesenberg 06.01.2023 20:32

Привет @SteveRiesenberg, спасибо. Вот сценарий, таблица MySQL содержит шаблоны URL, для которых пользователю разрешен доступ к ресурсу. Пример: (ПОСТ: /клиент). Я хочу проверить JWT по этим данным. (JWT имеет «авторитет»: [ { «роль»: «ROLE_vinay» } ], а MySQL имеет «авторитет» таблицы с ROLE_vinay в качестве первичного ключа и его URL-адрес и сведения о методе в других полях). После того, как я получу входящий Jwt и декодирую его, он подаст «авторитет», и я получу данные Role_vinay из MySQL, он содержит URL-адрес и сведения о методе. Я хочу сравнить это с входящим запросом.

Vinayak Pattar 09.01.2023 05:55

Это правильный путь или какие-либо другие стандартные способы, доступные для реализации, которую я делаю?

Vinayak Pattar 09.01.2023 05:55
Стоит ли изучать PHP в 2023-2024 годах?
Стоит ли изучать PHP в 2023-2024 годах?
Привет всем, сегодня я хочу высказать свои соображения по поводу вопроса, который я уже много раз получал в своем сообществе: "Стоит ли изучать PHP в...
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
В JavaScript одним из самых запутанных понятий является поведение ключевого слова "this" в стрелочной и обычной функциях.
Приемы CSS-макетирования - floats и Flexbox
Приемы CSS-макетирования - floats и Flexbox
Здравствуйте, друзья-студенты! Готовы совершенствовать свои навыки веб-дизайна? Сегодня в нашем путешествии мы рассмотрим приемы CSS-верстки - в...
Тестирование функциональных ngrx-эффектов в Angular 16 с помощью Jest
В системе управления состояниями ngrx, совместимой с Angular 16, появились функциональные эффекты. Это здорово и делает код определенно легче для...
Концепция локализации и ее применение в приложениях React ⚡️
Концепция локализации и ее применение в приложениях React ⚡️
Локализация - это процесс адаптации приложения к различным языкам и культурным требованиям. Это позволяет пользователям получить опыт, соответствующий...
Пользовательский скаляр GraphQL
Пользовательский скаляр GraphQL
Листовые узлы системы типов GraphQL называются скалярами. Достигнув скалярного типа, невозможно спуститься дальше по иерархии типов. Скалярный тип...
0
6
69
1
Перейти к ответу Данный вопрос помечен как решенный

Ответы 1

Ответ принят как подходящий

В своем последнем комментарии вы описали использование только MySQL, и я думаю, что это имеет смысл. Я не вижу, чтобы Redis вписывался в этот сценарий. Итак, поток:

  1. На сервере авторизации вы используете OAuth2TokenCustomizer<JwtEncodingContext>, чтобы добавить поле authority в JWT (например, ROLE_xyz).
  2. На сервере ресурсов вы используете JwtGrantedAuthoritiesConverter для сопоставления поля authority JWT с authentication.getAuthorities() в Spring Security.

Проблема заключается в том, что в вашей БД вы сопоставляете роли непосредственно с набором разрешенных URL-адресов (например, ROLE_xyz => POST /customer, ...). Возможно, вы имеете дело с существующей схемой базы данных и не можете спроектировать данные или отношения с нуля. Я ожидаю сопоставления ролей с набором разрешений (например, ROLE_xyz => customer.write, ...).

Если у вас есть такая схема разрешений, вы можете выполнить поиск в JwtGrantedAuthoritiesConverter выше и вместо этого сопоставить эти разрешения как полномочия.


Однако, если данные в MySQL сопоставляют роли с шаблонами URL-адресов, лучше всего использовать AuthorizationManager API для интеграции авторизации с вашей таблицей MySQL (для примера см. Настройка RequestMatcherDelegatingAuthorizationManager в разделе Авторизация запросов HttpServletRequests с помощью AuthorizationFilter).

Пользовательский AuthorizationManager может быть реализован для поиска ROLE_xyz и динамического создания списка правил авторизации с помощью RequestMatcherDelegatingAuthorizationManager.builder().

Например:

@Configuration
@EnableWebMvc
@EnableWebSecurity
public class SecurityConfiguration {

    @Bean
    SecurityFilterChain securityFilterChain(HttpSecurity http, AuthorizationManager<RequestAuthorizationContext> access)
            throws Exception {
        // @formatter:off
        http
            .authorizeHttpRequests((authorize) -> authorize
                .anyRequest().access(access)
            )
            .oauth2ResourceServer(OAuth2ResourceServerConfigurer::jwt);
        // @formatter:on

        return http.build();
    }

    @Bean
    AuthorizationManager<RequestAuthorizationContext> authorizationManager(
            HandlerMappingIntrospector introspector) {
        MvcRequestMatcher.Builder mvcMatcherBuilder = new MvcRequestMatcher.Builder(introspector);
        RequestMatcher anyRequest = AnyRequestMatcher.INSTANCE;

        AuthorizationManager<RequestAuthorizationContext> authenticated =
            AuthenticatedAuthorizationManager.authenticated();
        AuthorizationManager<RequestAuthorizationContext> denyAll =
            (a, c) -> new AuthorizationDecision(false);

        return (supplier, context) -> {
            Authentication authentication = supplier.get();
            HttpServletRequest request = context.getRequest();

            // TODO: Look up mappings in MySQL using authentication.getAuthorities()...

            RequestMatcherDelegatingAuthorizationManager.Builder builder =
                RequestMatcherDelegatingAuthorizationManager.builder();

            // TODO: Add custom mappings from MySQL...
            //builder.add(mvcMatcherBuilder.pattern("/customer/**"), authenticated);

            RequestMatcherDelegatingAuthorizationManager delegate =
                builder.add(anyRequest, denyAll).build();

            return delegate.check(() -> authentication, request);
        };
    }

}

Это может показаться сложным, но ваша схема авторизации (с использованием OAuth2 + JWT И ролей/полномочий И MySQL для управления ролями) представляет собой довольно сложную настройку. API AuthorizationManager на самом деле делает его проще, чем в более ранних версиях Spring Security.

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

Другие вопросы по теме